diff options
8 files changed, 1129 insertions, 0 deletions
diff --git a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider.cc b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider.cc new file mode 100644 index 0000000..7fbbdf3 --- /dev/null +++ b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider.cc @@ -0,0 +1,343 @@ +// 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.h" + +#include <vector> + +#include "base/logging.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_process_platform_part_chromeos.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" +#include "chrome/browser/chromeos/policy/ticl_device_settings_provider.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/profile_invalidation_provider_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/common/chrome_content_client.h" +#include "components/invalidation/invalidation_handler.h" +#include "components/invalidation/invalidation_service.h" +#include "components/invalidation/invalidation_state_tracker.h" +#include "components/invalidation/invalidator_state.h" +#include "components/invalidation/invalidator_storage.h" +#include "components/invalidation/profile_invalidation_provider.h" +#include "components/invalidation/ticl_invalidation_service.h" +#include "components/invalidation/ticl_settings_provider.h" +#include "components/policy/core/common/cloud/cloud_policy_constants.h" +#include "components/user_manager/user.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_service.h" +#include "google_apis/gaia/identity_provider.h" + +namespace policy { + +AffiliatedInvalidationServiceProvider::Consumer::~Consumer() { +} + +class AffiliatedInvalidationServiceProvider::InvalidationServiceObserver + : public syncer::InvalidationHandler { + public: + explicit InvalidationServiceObserver( + AffiliatedInvalidationServiceProvider* 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: + AffiliatedInvalidationServiceProvider* parent_; + invalidation::InvalidationService* invalidation_service_; + bool is_service_connected_; + bool is_observer_ready_; + + DISALLOW_COPY_AND_ASSIGN(InvalidationServiceObserver); +}; + +AffiliatedInvalidationServiceProvider::InvalidationServiceObserver:: + InvalidationServiceObserver( + AffiliatedInvalidationServiceProvider* 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; +} + +AffiliatedInvalidationServiceProvider::InvalidationServiceObserver:: + ~InvalidationServiceObserver() { + is_observer_ready_ = false; + invalidation_service_->UnregisterInvalidationHandler(this); +} + +invalidation::InvalidationService* +AffiliatedInvalidationServiceProvider::InvalidationServiceObserver:: + GetInvalidationService() { + return invalidation_service_; +} + +bool AffiliatedInvalidationServiceProvider::InvalidationServiceObserver:: + IsServiceConnected() const { + return is_service_connected_; +} + +void AffiliatedInvalidationServiceProvider::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 AffiliatedInvalidationServiceProvider::InvalidationServiceObserver:: + OnIncomingInvalidation( + const syncer::ObjectIdInvalidationMap& invalidation_map) { +} + +std::string AffiliatedInvalidationServiceProvider::InvalidationServiceObserver:: + GetOwnerName() const { + return "AffiliatedInvalidationService"; +} + +AffiliatedInvalidationServiceProvider::AffiliatedInvalidationServiceProvider() + : invalidation_service_(nullptr), + consumer_count_(0), + is_shut_down_(false) { + // The AffiliatedInvalidationServiceProvider 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()); +} + +AffiliatedInvalidationServiceProvider:: +~AffiliatedInvalidationServiceProvider() { + // Verify that the provider was shut down first. + DCHECK(is_shut_down_); +} + +void AffiliatedInvalidationServiceProvider::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::ProfileInvalidationProviderFactory::GetForProfile(profile); + if (!invalidation_provider) { + // If the Profile does not support invalidation (e.g. guest, incognito), + // ignore it. + return; + } + user_manager::User* user = + chromeos::ProfileHelper::Get()->GetUserByProfile(profile); + if (!user || + g_browser_process->platform_part()->browser_policy_connector_chromeos()-> + GetUserAffiliation(user->email()) != USER_AFFILIATION_MANAGED) { + // If the Profile belongs to a user who is not affiliated with the device's + // enrollment domain, 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( + new 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 AffiliatedInvalidationServiceProvider::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 AffiliatedInvalidationServiceProvider::UnregisterConsumer( + Consumer* consumer) { + if (!consumers_.HasObserver(consumer)) + return; + + consumers_.RemoveObserver(consumer); + --consumer_count_; + + if (invalidation_service_ && consumer_count_ == 0) { + invalidation_service_ = nullptr; + DestroyDeviceInvalidationService(); + } +} + +void AffiliatedInvalidationServiceProvider::Shutdown() { + is_shut_down_ = true; + + registrar_.RemoveAll(); + profile_invalidation_service_observers_.clear(); + DestroyDeviceInvalidationService(); + + if (invalidation_service_) { + invalidation_service_ = nullptr; + // Explicitly notify consumers that the invalidation service they were using + // is no longer available. + SetInvalidationService(nullptr); + } +} + +invalidation::TiclInvalidationService* +AffiliatedInvalidationServiceProvider:: + GetDeviceInvalidationServiceForTest() const { + return device_invalidation_service_.get(); +} + +void AffiliatedInvalidationServiceProvider::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; + } + + if (invalidation_service != device_invalidation_service_.get()) { + // If an invalidation service other than the device-global one connected, + // destroy the device-global service. + invalidation_service_ = nullptr; + DestroyDeviceInvalidationService(); + } + + // Make the invalidation service that just connected available to consumers. + SetInvalidationService(invalidation_service); +} + +void AffiliatedInvalidationServiceProvider::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 AffiliatedInvalidationServiceProvider::FindConnectedInvalidationService() { + DCHECK(!invalidation_service_); + DCHECK(consumer_count_); + DCHECK(!is_shut_down_); + + for (ScopedVector<InvalidationServiceObserver>::const_iterator it = + profile_invalidation_service_observers_.begin(); + it != profile_invalidation_service_observers_.end(); ++it) { + if ((*it)->IsServiceConnected()) { + // If a connected invalidation service belonging to an affiliated + // logged-in user is found, make it available to consumers. + DestroyDeviceInvalidationService(); + SetInvalidationService((*it)->GetInvalidationService()); + return; + } + } + + if (!device_invalidation_service_) { + // If no other connected invalidation service was found and no device-global + // invalidation service exists, create one. + device_invalidation_service_.reset( + new invalidation::TiclInvalidationService( + GetUserAgent(), + scoped_ptr<IdentityProvider>(new chromeos::DeviceIdentityProvider( + chromeos::DeviceOAuth2TokenServiceFactory::Get())), + scoped_ptr<invalidation::TiclSettingsProvider>( + new TiclDeviceSettingsProvider), + g_browser_process->gcm_driver(), + g_browser_process->system_request_context())); + device_invalidation_service_->Init( + scoped_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 AffiliatedInvalidationServiceProvider::SetInvalidationService( + invalidation::InvalidationService* invalidation_service) { + DCHECK(!invalidation_service_); + invalidation_service_ = invalidation_service; + FOR_EACH_OBSERVER(Consumer, + consumers_, + OnInvalidationServiceSet(invalidation_service_)); +} + +void AffiliatedInvalidationServiceProvider::DestroyDeviceInvalidationService() { + device_invalidation_service_observer_.reset(); + device_invalidation_service_.reset(); +} + +} // namespace policy diff --git a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider.h b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider.h new file mode 100644 index 0000000..5484355 --- /dev/null +++ b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider.h @@ -0,0 +1,140 @@ +// 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. + +#ifndef CHROME_BROWSER_CHROMEOS_POLICY_AFFILIATED_INVALIDATION_SERVICE_PROVIDER_H_ +#define CHROME_BROWSER_CHROMEOS_POLICY_AFFILIATED_INVALIDATION_SERVICE_PROVIDER_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/observer_list.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" + +namespace invalidation { +class InvalidationService; +class TiclInvalidationService; +} + +namespace policy { + +// This class provides access to an |InvalidationService| that can be used to +// subscribe to invalidations generated by the device's enrollment domain, e.g. +// policy pushing and remote commands for: +// * the device itself +// * device-local accounts +// * other users affiliated with the enrollment domain +// +// If an affiliated user with a connected invalidation service is logged in, +// that invalidation service will be used to conserve server resources. If there +// are no logged-in users matching these criteria, a device-global +// |TiclInvalidationService| is spun up. +// The class monitors the status of the invalidation services and switches +// between them whenever the service currently in use disconnects or the +// device-global invalidation service can be replaced with another service that +// just connected. +class AffiliatedInvalidationServiceProvider + : public content::NotificationObserver { + public: + class Consumer { + public: + // This method is called when the invalidation service that the consumer + // should use changes: + // * If |invalidation_service| is a nullptr, no invalidation service is + // currently available for use. + // * Otherwise, |invalidation_service| is the invalidation service that the + // consumer should use. It is guaranteed to be connected. Any previously + // provided invalidation services must no longer be used. + virtual void OnInvalidationServiceSet( + invalidation::InvalidationService* invalidation_service) = 0; + + protected: + virtual ~Consumer(); + }; + + AffiliatedInvalidationServiceProvider(); + ~AffiliatedInvalidationServiceProvider() override; + + // content::NotificationObserver: + void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) override; + + // Indicates that |consumer| is interested in using the shared + // |InvalidationService|. The consumer's OnInvalidationServiceSet() method + // will be called back when a connected invalidation service becomes + // available. If an invalidation service is available already, the callback + // will occur synchronously. The |consumer| must be unregistered before |this| + // is destroyed. + void RegisterConsumer(Consumer* consumer); + + // Indicates that |consumer| is no longer interested in using the + // shared |InvalidationService|. + void UnregisterConsumer(Consumer* consumer); + + // Shuts down the provider. Once the provider is shut down, it no longer makes + // any invalidation service available to consumers, no longer observes any + // per-profile invalidation services and no longer maintains a device-global + // invalidation service. + void Shutdown(); + + invalidation::TiclInvalidationService* + GetDeviceInvalidationServiceForTest() const; + + private: + // Helper that monitors the status of a single |InvalidationService|. + class InvalidationServiceObserver; + + // Status updates received from |InvalidationServiceObserver|s. + void OnInvalidationServiceConnected( + invalidation::InvalidationService* invalidation_service); + void OnInvalidationServiceDisconnected( + invalidation::InvalidationService* invalidation_service); + + // Checks whether a connected |InvalidationService| affiliated with the + // device's enrollment domain is available. If so, notifies the consumers. + // Otherwise, consumers will be notified once such an invalidation service + // becomes available. + // Further ensures that a device-global invalidation service is running iff + // there is no other connected service available for use and there is at least + // one registered consumer. + void FindConnectedInvalidationService(); + + // Choose |invalidation_service| as the shared invalidation service and notify + // consumers. + void SetInvalidationService( + invalidation::InvalidationService* invalidation_service); + + // Destroy the device-global invalidation service, if any. + void DestroyDeviceInvalidationService(); + + content::NotificationRegistrar registrar_; + + // Device-global invalidation service. + scoped_ptr<invalidation::TiclInvalidationService> + device_invalidation_service_; + + // State observer for the device-global invalidation service. + scoped_ptr<InvalidationServiceObserver> device_invalidation_service_observer_; + + // State observers for logged-in users' invalidation services. + ScopedVector<InvalidationServiceObserver> + profile_invalidation_service_observers_; + + // The invalidation service currently used by consumers. nullptr if there are + // no registered consumers or no connected invalidation service is available + // for use. + invalidation::InvalidationService* invalidation_service_; + + ObserverList<Consumer, true> consumers_; + int consumer_count_; + + bool is_shut_down_; + + DISALLOW_COPY_AND_ASSIGN(AffiliatedInvalidationServiceProvider); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_CHROMEOS_POLICY_AFFILIATED_INVALIDATION_SERVICE_PROVIDER_H_ diff --git a/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_unittest.cc b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_unittest.cc new file mode 100644 index 0000000..5c1cb60 --- /dev/null +++ b/chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_unittest.cc @@ -0,0 +1,627 @@ +// 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.h" + +#include <string> + +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/chromeos/login/users/fake_user_manager.h" +#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" +#include "chrome/browser/chromeos/policy/stub_enterprise_install_attributes.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" +#include "chrome/browser/chromeos/settings/device_settings_service.h" +#include "chrome/browser/invalidation/fake_invalidation_service.h" +#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "chromeos/cryptohome/system_salt_getter.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "components/invalidation/invalidation_service.h" +#include "components/invalidation/invalidator_state.h" +#include "components/invalidation/profile_invalidation_provider.h" +#include "components/invalidation/ticl_invalidation_service.h" +#include "components/keyed_service/core/keyed_service.h" +#include "components/policy/core/common/cloud/cloud_policy_constants.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_service.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Mock; +using testing::StrictMock; + +namespace policy { + +namespace { + +const char kAffiliatedUserID1[] = "test_1@example.com"; +const char kAffiliatedUserID2[] = "test_2@example.com"; +const char kUnaffiliatedUserID[] = "test@other_domain.test"; + +KeyedService* BuildProfileInvalidationProvider( + content::BrowserContext* context) { + scoped_ptr<invalidation::FakeInvalidationService> invalidation_service( + new invalidation::FakeInvalidationService); + invalidation_service->SetInvalidatorState( + syncer::TRANSIENT_INVALIDATION_ERROR); + return new invalidation::ProfileInvalidationProvider( + invalidation_service.Pass()); +} + +} // namespace + +class MockConsumer : public AffiliatedInvalidationServiceProvider::Consumer { + public: + MockConsumer(); + ~MockConsumer() override; + + MOCK_METHOD1(OnInvalidationServiceSet, + void(invalidation::InvalidationService*)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockConsumer); +}; + +class AffiliatedInvalidationServiceProviderTest : public testing::Test { + public: + AffiliatedInvalidationServiceProviderTest(); + + // testing::Test: + virtual void SetUp() override; + virtual void TearDown() override; + + // Ownership is not passed. The Profile is owned by the global ProfileManager. + Profile* LogInAndReturnProfile(const std::string& user_id); + + // Logs in as an affiliated user and indicates that the per-profile + // invalidation service for this user connected. Verifies that this + // invalidation service is made available to the |consumer_| and the + // device-global invalidation service is destroyed. + void LogInAsAffiliatedUserAndConnectInvalidationService(); + + // Logs in as an unaffiliated user and indicates that the per-profile + // invalidation service for this user connected. Verifies that this + // invalidation service is ignored and the device-global invalidation service + // is not destroyed. + void LogInAsUnaffiliatedUserAndConnectInvalidationService(); + + // Indicates that the device-global invalidation service connected. Verifies + // that the |consumer_| is informed about this. + void ConnectDeviceGlobalInvalidationService(); + + // Indicates that the logged-in user's per-profile invalidation service + // disconnected. Verifies that the |consumer_| is informed about this and a + // device-global invalidation service is created. + void DisconnectPerProfileInvalidationService(); + + invalidation::FakeInvalidationService* GetProfileInvalidationService( + Profile* profile, + bool create); + + protected: + scoped_ptr<AffiliatedInvalidationServiceProvider> provider_; + StrictMock<MockConsumer> consumer_; + invalidation::TiclInvalidationService* device_invalidation_service_; + invalidation::FakeInvalidationService* profile_invalidation_service_; + + private: + content::TestBrowserThreadBundle thread_bundle_; + chromeos::FakeUserManager* fake_user_manager_; + chromeos::ScopedUserManagerEnabler user_manager_enabler_; + ScopedStubEnterpriseInstallAttributes install_attributes_; + scoped_ptr<chromeos::ScopedTestDeviceSettingsService> + test_device_settings_service_; + scoped_ptr<chromeos::ScopedTestCrosSettings> test_cros_settings_; + TestingProfileManager profile_manager_; +}; + +MockConsumer::MockConsumer() { +} + +MockConsumer::~MockConsumer() { +} + +AffiliatedInvalidationServiceProviderTest:: +AffiliatedInvalidationServiceProviderTest() + : device_invalidation_service_(nullptr), + profile_invalidation_service_(nullptr), + fake_user_manager_(new chromeos::FakeUserManager), + user_manager_enabler_(fake_user_manager_), + install_attributes_("example.com", + "user@example.com", + "device_id", + DEVICE_MODE_ENTERPRISE), + profile_manager_(TestingBrowserProcess::GetGlobal()) { +} + +void AffiliatedInvalidationServiceProviderTest::SetUp() { + chromeos::SystemSaltGetter::Initialize(); + chromeos::DBusThreadManager::Initialize(); + chromeos::DeviceOAuth2TokenServiceFactory::Initialize(); + ASSERT_TRUE(profile_manager_.SetUp()); + + test_device_settings_service_.reset(new + chromeos::ScopedTestDeviceSettingsService); + test_cros_settings_.reset(new chromeos::ScopedTestCrosSettings); + + invalidation::ProfileInvalidationProviderFactory::GetInstance()-> + RegisterTestingFactory(BuildProfileInvalidationProvider); + + provider_.reset(new AffiliatedInvalidationServiceProvider); +} + +void AffiliatedInvalidationServiceProviderTest::TearDown() { + provider_->Shutdown(); + provider_.reset(); + + invalidation::ProfileInvalidationProviderFactory::GetInstance()-> + RegisterTestingFactory(nullptr); + chromeos::DeviceOAuth2TokenServiceFactory::Shutdown(); + chromeos::DBusThreadManager::Shutdown(); + chromeos::SystemSaltGetter::Shutdown(); +} + +Profile* AffiliatedInvalidationServiceProviderTest::LogInAndReturnProfile( + const std::string& user_id) { + fake_user_manager_->AddUser(user_id); + Profile* profile = profile_manager_.CreateTestingProfile(user_id); + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, + content::NotificationService::AllSources(), + content::Details<Profile>(profile)); + return profile; +} + +void AffiliatedInvalidationServiceProviderTest:: + LogInAsAffiliatedUserAndConnectInvalidationService() { + Mock::VerifyAndClearExpectations(&consumer_); + + // Log in as an affiliated user. + Profile* profile = LogInAndReturnProfile(kAffiliatedUserID1); + EXPECT_TRUE(profile); + Mock::VerifyAndClearExpectations(&consumer_); + + // Verify that a per-profile invalidation service has been created. + profile_invalidation_service_ = + GetProfileInvalidationService(profile, false /* create */); + ASSERT_TRUE(profile_invalidation_service_); + + // Verify that the device-global invalidation service still exists. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + // Indicate that the per-profile invalidation service has connected. Verify + // that the consumer is informed about this. + EXPECT_CALL(consumer_, + OnInvalidationServiceSet(profile_invalidation_service_)).Times(1); + profile_invalidation_service_->SetInvalidatorState( + syncer::INVALIDATIONS_ENABLED); + Mock::VerifyAndClearExpectations(&consumer_); + + // Verify that the device-global invalidation service has been destroyed. + EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest()); + + Mock::VerifyAndClearExpectations(&consumer_); +} + +void AffiliatedInvalidationServiceProviderTest:: + LogInAsUnaffiliatedUserAndConnectInvalidationService() { + Mock::VerifyAndClearExpectations(&consumer_); + + // Log in as an unaffiliated user. + Profile* profile = LogInAndReturnProfile(kUnaffiliatedUserID); + EXPECT_TRUE(profile); + + // Verify that a per-profile invalidation service has been created. + profile_invalidation_service_ = + GetProfileInvalidationService(profile, false /* create */); + ASSERT_TRUE(profile_invalidation_service_); + + // Verify that the device-global invalidation service still exists. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + // Indicate that the per-profile invalidation service has connected. Verify + // that the consumer is not called back. + profile_invalidation_service_->SetInvalidatorState( + syncer::INVALIDATIONS_ENABLED); + + // Verify that the device-global invalidation service still exists. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + Mock::VerifyAndClearExpectations(&consumer_); +} + +void AffiliatedInvalidationServiceProviderTest:: + ConnectDeviceGlobalInvalidationService() { + Mock::VerifyAndClearExpectations(&consumer_); + + // Verify that a device-global invalidation service has been created. + device_invalidation_service_ = + provider_->GetDeviceInvalidationServiceForTest(); + ASSERT_TRUE(device_invalidation_service_); + + // Indicate that the device-global invalidation service has connected. Verify + // that the consumer is informed about this. + EXPECT_CALL(consumer_, OnInvalidationServiceSet(device_invalidation_service_)) + .Times(1); + device_invalidation_service_->OnInvalidatorStateChange( + syncer::INVALIDATIONS_ENABLED); + + Mock::VerifyAndClearExpectations(&consumer_); +} + +void AffiliatedInvalidationServiceProviderTest:: + DisconnectPerProfileInvalidationService() { + Mock::VerifyAndClearExpectations(&consumer_); + + ASSERT_TRUE(profile_invalidation_service_); + + // Indicate that the per-profile invalidation service has disconnected. Verify + // that the consumer is informed about this. + EXPECT_CALL(consumer_, OnInvalidationServiceSet(nullptr)).Times(1); + profile_invalidation_service_->SetInvalidatorState( + syncer::INVALIDATION_CREDENTIALS_REJECTED); + + // Verify that a device-global invalidation service has been created. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + Mock::VerifyAndClearExpectations(&consumer_); +} + +invalidation::FakeInvalidationService* +AffiliatedInvalidationServiceProviderTest::GetProfileInvalidationService( + Profile* profile, bool create) { + invalidation::ProfileInvalidationProvider* invalidation_provider = + static_cast<invalidation::ProfileInvalidationProvider*>( + invalidation::ProfileInvalidationProviderFactory::GetInstance()-> + GetServiceForBrowserContext(profile, create)); + if (!invalidation_provider) + return nullptr; + return static_cast<invalidation::FakeInvalidationService*>( + invalidation_provider->GetInvalidationService()); +} + +// No consumers are registered with the AffiliatedInvalidationServiceProvider. +// Verifies that no device-global invalidation service is created, whether an +// affiliated user is logged in or not. +TEST_F(AffiliatedInvalidationServiceProviderTest, NoConsumers) { + // Verify that no device-global invalidation service has been created. + EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest()); + + // Log in as an affiliated user. + EXPECT_TRUE(LogInAndReturnProfile(kAffiliatedUserID1)); + + // Verify that no device-global invalidation service has been created. + EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest()); +} + +// A consumer is registered with the AffiliatedInvalidationServiceProvider. +// Verifies that when no per-profile invalidation service belonging to an +// affiliated user is available, a device-global invalidation service is +// created. Further verifies that when the device-global invalidation service +// connects, it is made available to the consumer. +TEST_F(AffiliatedInvalidationServiceProviderTest, + UseDeviceInvalidationService) { + // Register a consumer. Verify that the consumer is not called back + // immediately as no connected invalidation service exists yet. + provider_->RegisterConsumer(&consumer_); + + // Indicate that the device-global invalidation service connected. Verify that + // that the consumer is informed about this. + ConnectDeviceGlobalInvalidationService(); + + // Indicate that the device-global invalidation service has disconnected. + // Verify that the consumer is informed about this. + EXPECT_CALL(consumer_, OnInvalidationServiceSet(nullptr)).Times(1); + device_invalidation_service_->OnInvalidatorStateChange( + syncer::INVALIDATION_CREDENTIALS_REJECTED); + Mock::VerifyAndClearExpectations(&consumer_); + + // Verify that the device-global invalidation service still exists. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + // Unregister the consumer. + provider_->UnregisterConsumer(&consumer_); + Mock::VerifyAndClearExpectations(&consumer_); +} + +// A consumer is registered with the AffiliatedInvalidationServiceProvider. +// Verifies that when a per-profile invalidation service belonging to an +// affiliated user connects, it is made available to the consumer. +TEST_F(AffiliatedInvalidationServiceProviderTest, + UseAffiliatedProfileInvalidationService) { + // Register a consumer. Verify that the consumer is not called back + // immediately as no connected invalidation service exists yet. + provider_->RegisterConsumer(&consumer_); + + // Verify that a device-global invalidation service has been created. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + // Log in as an affiliated user and indicate that the per-profile invalidation + // service for this user connected. Verify that this invalidation service is + // made available to the |consumer_| and the device-global invalidation + // service is destroyed. + LogInAsAffiliatedUserAndConnectInvalidationService(); + + // Indicate that the logged-in user's per-profile invalidation service + // disconnected. Verify that the consumer is informed about this and a + // device-global invalidation service is created. + DisconnectPerProfileInvalidationService(); + + // Unregister the consumer. + provider_->UnregisterConsumer(&consumer_); + Mock::VerifyAndClearExpectations(&consumer_); +} + +// A consumer is registered with the AffiliatedInvalidationServiceProvider. +// Verifies that when a per-profile invalidation service belonging to an +// unaffiliated user connects, it is ignored. +TEST_F(AffiliatedInvalidationServiceProviderTest, + DoNotUseUnaffiliatedProfileInvalidationService) { + // Register a consumer. Verify that the consumer is not called back + // immediately as no connected invalidation service exists yet. + provider_->RegisterConsumer(&consumer_); + + // Verify that a device-global invalidation service has been created. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + // Log in as an unaffiliated user and indicate that the per-profile + // invalidation service for this user connected. Verify that this invalidation + // service is ignored and the device-global invalidation service is not + // destroyed. + LogInAsUnaffiliatedUserAndConnectInvalidationService(); + + // Unregister the consumer. + provider_->UnregisterConsumer(&consumer_); + Mock::VerifyAndClearExpectations(&consumer_); +} + +// A consumer is registered with the AffiliatedInvalidationServiceProvider. A +// device-global invalidation service exists, is connected and is made available +// to the consumer. Verifies that when a per-profile invalidation service +// belonging to an affiliated user connects, it is made available to the +// consumer instead and the device-global invalidation service is destroyed. +TEST_F(AffiliatedInvalidationServiceProviderTest, + SwitchToAffiliatedProfileInvalidationService) { + // Register a consumer. Verify that the consumer is not called back + // immediately as no connected invalidation service exists yet. + provider_->RegisterConsumer(&consumer_); + + // Indicate that the device-global invalidation service connected. Verify that + // that the consumer is informed about this. + ConnectDeviceGlobalInvalidationService(); + + // Log in as an affiliated user and indicate that the per-profile invalidation + // service for this user connected. Verify that this invalidation service is + // made available to the |consumer_| and the device-global invalidation + // service is destroyed. + LogInAsAffiliatedUserAndConnectInvalidationService(); + + // Unregister the consumer. + provider_->UnregisterConsumer(&consumer_); + Mock::VerifyAndClearExpectations(&consumer_); +} + +// A consumer is registered with the AffiliatedInvalidationServiceProvider. A +// device-global invalidation service exists, is connected and is made available +// to the consumer. Verifies that when a per-profile invalidation service +// belonging to an unaffiliated user connects, it is ignored and the +// device-global invalidation service continues to be made available to the +// consumer. +TEST_F(AffiliatedInvalidationServiceProviderTest, + DoNotSwitchToUnaffiliatedProfileInvalidationService) { + // Register a consumer. Verify that the consumer is not called back + // immediately as no connected invalidation service exists yet. + provider_->RegisterConsumer(&consumer_); + + // Indicate that the device-global invalidation service connected. Verify that + // that the consumer is informed about this. + ConnectDeviceGlobalInvalidationService(); + + // Log in as an unaffiliated user and indicate that the per-profile + // invalidation service for this user connected. Verify that this invalidation + // service is ignored and the device-global invalidation service is not + // destroyed. + LogInAsUnaffiliatedUserAndConnectInvalidationService(); + + // Unregister the consumer. + provider_->UnregisterConsumer(&consumer_); + Mock::VerifyAndClearExpectations(&consumer_); +} + +// A consumer is registered with the AffiliatedInvalidationServiceProvider. A +// per-profile invalidation service belonging to an affiliated user exists, is +// connected and is made available to the consumer. Verifies that when the +// per-profile invalidation service disconnects, a device-global invalidation +// service is created. Further verifies that when the device-global invalidation +// service connects, it is made available to the consumer. +TEST_F(AffiliatedInvalidationServiceProviderTest, + SwitchToDeviceInvalidationService) { + // Register a consumer. Verify that the consumer is not called back + // immediately as no connected invalidation service exists yet. + provider_->RegisterConsumer(&consumer_); + + // Verify that a device-global invalidation service has been created. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + // Log in as an affiliated user and indicate that the per-profile invalidation + // service for this user connected. Verify that this invalidation service is + // made available to the |consumer_| and the device-global invalidation + // service is destroyed. + LogInAsAffiliatedUserAndConnectInvalidationService(); + + // Indicate that the logged-in user's per-profile invalidation service + // disconnected. Verify that the consumer is informed about this and a + // device-global invalidation service is created. + DisconnectPerProfileInvalidationService(); + + // Indicate that the device-global invalidation service connected. Verify that + // that the consumer is informed about this. + ConnectDeviceGlobalInvalidationService(); + + // Unregister the consumer. + provider_->UnregisterConsumer(&consumer_); + Mock::VerifyAndClearExpectations(&consumer_); +} + +// A consumer is registered with the AffiliatedInvalidationServiceProvider. A +// per-profile invalidation service belonging to a first affiliated user exists, +// is connected and is made available to the consumer. A per-profile +// invalidation service belonging to a second affiliated user also exists and is +// connected. Verifies that when the per-profile invalidation service belonging +// to the first user disconnects, the per-profile invalidation service belonging +// to the second user is made available to the consumer instead. +TEST_F(AffiliatedInvalidationServiceProviderTest, + SwitchBetweenAffiliatedProfileInvalidationServices) { + // Register a consumer. Verify that the consumer is not called back + // immediately as no connected invalidation service exists yet. + provider_->RegisterConsumer(&consumer_); + + // Verify that a device-global invalidation service has been created. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + // Log in as a first affiliated user and indicate that the per-profile + // invalidation service for this user connected. Verify that this invalidation + // service is made available to the |consumer_| and the device-global + // invalidation service is destroyed. + LogInAsAffiliatedUserAndConnectInvalidationService(); + + // Log in as a second affiliated user. + Profile* second_profile = LogInAndReturnProfile(kAffiliatedUserID2); + EXPECT_TRUE(second_profile); + + // Verify that the device-global invalidation service still does not exist. + EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest()); + + // Verify that a per-profile invalidation service for the second user has been + // created. + invalidation::FakeInvalidationService* second_profile_invalidation_service = + GetProfileInvalidationService(second_profile, false /* create */); + ASSERT_TRUE(second_profile_invalidation_service); + + // Indicate that the second user's per-profile invalidation service has + // connected. Verify that the consumer is not called back. + second_profile_invalidation_service->SetInvalidatorState( + syncer::INVALIDATIONS_ENABLED); + Mock::VerifyAndClearExpectations(&consumer_); + + // Indicate that the first user's per-profile invalidation service has + // disconnected. Verify that the consumer is informed that the second user's + // per-profile invalidation service should be used instead of the first + // user's. + EXPECT_CALL(consumer_, + OnInvalidationServiceSet(second_profile_invalidation_service)) + .Times(1); + profile_invalidation_service_->SetInvalidatorState( + syncer::INVALIDATION_CREDENTIALS_REJECTED); + Mock::VerifyAndClearExpectations(&consumer_); + + // Verify that the device-global invalidation service still does not exist. + EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest()); + + // Unregister the consumer. + provider_->UnregisterConsumer(&consumer_); + Mock::VerifyAndClearExpectations(&consumer_); +} + +// A consumer is registered with the AffiliatedInvalidationServiceProvider. A +// device-global invalidation service exists, is connected and is made available +// to the consumer. Verifies that when a second consumer registers, the +// device-global invalidation service is made available to it as well. Further +// verifies that when the first consumer unregisters, the device-global +// invalidation service is not destroyed and remains available to the second +// consumer. Further verifies that when the second consumer also unregisters, +// the device-global invalidation service is destroyed. +TEST_F(AffiliatedInvalidationServiceProviderTest, MultipleConsumers) { + // Register a first consumer. Verify that the consumer is not called back + // immediately as no connected invalidation service exists yet. + provider_->RegisterConsumer(&consumer_); + + // Indicate that the device-global invalidation service connected. Verify that + // that the consumer is informed about this. + ConnectDeviceGlobalInvalidationService(); + + // Register a second consumer. Verify that the consumer is called back + // immediately as a connected invalidation service is available. + StrictMock<MockConsumer> second_consumer; + EXPECT_CALL(second_consumer, + OnInvalidationServiceSet(device_invalidation_service_)).Times(1); + provider_->RegisterConsumer(&second_consumer); + Mock::VerifyAndClearExpectations(&second_consumer); + + // Unregister the first consumer. + provider_->UnregisterConsumer(&consumer_); + + // Verify that the device-global invalidation service still exists. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + // Unregister the second consumer. + provider_->UnregisterConsumer(&second_consumer); + + // Verify that the device-global invalidation service has been destroyed. + EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest()); + Mock::VerifyAndClearExpectations(&consumer_); + Mock::VerifyAndClearExpectations(&second_consumer); +} + +// A consumer is registered with the AffiliatedInvalidationServiceProvider. A +// per-profile invalidation service belonging to a first affiliated user exists, +// is connected and is made available to the consumer. Verifies that when the +// provider is shut down, the consumer is informed that no invalidation service +// is available for use anymore. Also verifies that no device-global +// invalidation service is created and a per-profile invalidation service +// belonging to a second affiliated user that subsequently connects is ignored. +TEST_F(AffiliatedInvalidationServiceProviderTest, NoServiceAfterShutdown) { + // Register a consumer. Verify that the consumer is not called back + // immediately as no connected invalidation service exists yet. + provider_->RegisterConsumer(&consumer_); + + // Verify that a device-global invalidation service has been created. + EXPECT_TRUE(provider_->GetDeviceInvalidationServiceForTest()); + + // Log in as a first affiliated user and indicate that the per-profile + // invalidation service for this user connected. Verify that this invalidation + // service is made available to the |consumer_| and the device-global + // invalidation service is destroyed. + LogInAsAffiliatedUserAndConnectInvalidationService(); + + // Shut down the |provider_|. Verify that the |consumer_| is informed that no + // invalidation service is available for use anymore. + EXPECT_CALL(consumer_, OnInvalidationServiceSet(nullptr)).Times(1); + provider_->Shutdown(); + Mock::VerifyAndClearExpectations(&consumer_); + + // Verify that the device-global invalidation service still does not exist. + EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest()); + + // Log in as a second affiliated user. + Profile* second_profile = LogInAndReturnProfile(kAffiliatedUserID2); + EXPECT_TRUE(second_profile); + + // Verify that the device-global invalidation service still does not exist. + EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest()); + + // Create a per-profile invalidation service for the second user. + invalidation::FakeInvalidationService* second_profile_invalidation_service = + GetProfileInvalidationService(second_profile, true /* create */); + ASSERT_TRUE(second_profile_invalidation_service); + + // Indicate that the second user's per-profile invalidation service has + // connected. Verify that the consumer is not called back. + second_profile_invalidation_service->SetInvalidatorState( + syncer::INVALIDATIONS_ENABLED); + + // Verify that the device-global invalidation service still does not exist. + EXPECT_FALSE(provider_->GetDeviceInvalidationServiceForTest()); + + // Unregister the consumer. + provider_->UnregisterConsumer(&consumer_); + Mock::VerifyAndClearExpectations(&consumer_); +} + +} // namespace policy diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc index dd7dd53..9c91387 100644 --- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc +++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.cc @@ -17,6 +17,7 @@ #include "base/sequenced_task_runner.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/sequenced_worker_pool.h" +#include "chrome/browser/chromeos/policy/affiliated_invalidation_service_provider.h" #include "chrome/browser/chromeos/policy/consumer_management_service.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_initializer.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_invalidator.h" @@ -152,6 +153,9 @@ void BrowserPolicyConnectorChromeOS::Init( local_state_ = local_state; ChromeBrowserPolicyConnector::Init(local_state, request_context); + affiliated_invalidation_service_provider_.reset( + new AffiliatedInvalidationServiceProvider); + const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(chromeos::switches::kEnableConsumerManagement)) { @@ -201,6 +205,13 @@ void BrowserPolicyConnectorChromeOS::Init( } void BrowserPolicyConnectorChromeOS::PreShutdown() { + // Let the |affiliated_invalidation_service_provider_| unregister itself as an + // observer of per-Profile InvalidationServices and the device-global + // invalidation::TiclInvalidationService it may have created as an observer of + // the DeviceOAuth2TokenService that is destroyed before Shutdown() is called. + if (affiliated_invalidation_service_provider_) + affiliated_invalidation_service_provider_->Shutdown(); + // Let the |device_cloud_policy_invalidator_| unregister itself as an // observer of per-Profile InvalidationServices and the device-global // invalidation::TiclInvalidationService it may have created as an observer of diff --git a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h index 1917e53..cb3ec6f 100644 --- a/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h +++ b/chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h @@ -24,6 +24,7 @@ class URLRequestContextGetter; namespace policy { +class AffiliatedInvalidationServiceProvider; class ConsumerManagementService; class DeviceCloudPolicyInitializer; class DeviceCloudPolicyInvalidator; @@ -150,6 +151,8 @@ class BrowserPolicyConnectorChromeOS // Components of the device cloud policy implementation. scoped_ptr<ServerBackedStateKeysBroker> state_keys_broker_; scoped_ptr<EnterpriseInstallAttributes> install_attributes_; + scoped_ptr<AffiliatedInvalidationServiceProvider> + affiliated_invalidation_service_provider_; scoped_ptr<ConsumerManagementService> consumer_management_service_; DeviceCloudPolicyManagerChromeOS* device_cloud_policy_manager_; PrefService* local_state_; diff --git a/chrome/browser/invalidation/profile_invalidation_provider_factory.h b/chrome/browser/invalidation/profile_invalidation_provider_factory.h index 49c5e84..2e45328 100644 --- a/chrome/browser/invalidation/profile_invalidation_provider_factory.h +++ b/chrome/browser/invalidation/profile_invalidation_provider_factory.h @@ -10,6 +10,7 @@ #include "components/keyed_service/content/browser_context_keyed_service_factory.h" namespace policy { +class AffiliatedInvalidationServiceProviderTest; class DeviceCloudPolicyInvalidatorTest; } @@ -48,6 +49,7 @@ class ProfileInvalidationProviderFactory private: friend class ProfileInvalidationProviderFactoryTestBase; + friend class policy::AffiliatedInvalidationServiceProviderTest; friend class policy::DeviceCloudPolicyInvalidatorTest; friend struct DefaultSingletonTraits<ProfileInvalidationProviderFactory>; diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index efcf2de..c751c02 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi @@ -727,6 +727,8 @@ 'browser/chromeos/platform_keys/platform_keys_service.h', 'browser/chromeos/platform_keys/platform_keys_service_factory.cc', 'browser/chromeos/platform_keys/platform_keys_service_factory.h', + 'browser/chromeos/policy/affiliated_invalidation_service_provider.cc', + 'browser/chromeos/policy/affiliated_invalidation_service_provider.h', 'browser/chromeos/policy/auto_enrollment_client.cc', 'browser/chromeos/policy/auto_enrollment_client.h', 'browser/chromeos/policy/browser_policy_connector_chromeos.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index a235300..e220c63 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1244,6 +1244,7 @@ 'browser/chromeos/ownership/fake_owner_settings_service.cc', 'browser/chromeos/ownership/fake_owner_settings_service.h', 'browser/chromeos/ownership/owner_settings_service_chromeos_unittest.cc', + 'browser/chromeos/policy/affiliated_invalidation_service_provider_unittest.cc', 'browser/chromeos/policy/auto_enrollment_client_unittest.cc', 'browser/chromeos/policy/cloud_external_data_manager_base_unittest.cc', 'browser/chromeos/policy/cloud_external_data_policy_observer_unittest.cc', |