blob: 248d7093412bd0b4e704d2d756c412a84e72ed9c [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/policy/cloud/remote_commands_invalidator.h"
#include <string>
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/scoped_observation.h"
#include "chrome/browser/policy/cloud/policy_invalidation_util.h"
#include "components/invalidation/public/invalidation.h"
#include "components/invalidation/public/invalidation_service.h"
#include "components/invalidation/public/invalidation_util.h"
#include "components/invalidation/public/invalidator_state.h"
#include "components/invalidation/public/single_topic_invalidation_set.h"
#include "components/invalidation/public/topic_invalidation_map.h"
#include "components/policy/core/common/cloud/enterprise_metrics.h"
namespace policy {
RemoteCommandsInvalidator::RemoteCommandsInvalidator(std::string owner_name)
: owner_name_(std::move(owner_name)) {}
RemoteCommandsInvalidator::~RemoteCommandsInvalidator() {
DCHECK_EQ(SHUT_DOWN, state_);
}
void RemoteCommandsInvalidator::Initialize(
invalidation::InvalidationService* invalidation_service) {
DCHECK_EQ(SHUT_DOWN, state_);
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(invalidation_service);
invalidation_service_ = invalidation_service;
state_ = STOPPED;
OnInitialize();
}
void RemoteCommandsInvalidator::Shutdown() {
DCHECK_NE(SHUT_DOWN, state_);
DCHECK(thread_checker_.CalledOnValidThread());
Stop();
state_ = SHUT_DOWN;
OnShutdown();
}
void RemoteCommandsInvalidator::Start() {
DCHECK_EQ(STOPPED, state_);
DCHECK(thread_checker_.CalledOnValidThread());
state_ = STARTED;
OnStart();
}
void RemoteCommandsInvalidator::Stop() {
DCHECK_NE(SHUT_DOWN, state_);
DCHECK(thread_checker_.CalledOnValidThread());
if (state_ == STARTED) {
Unregister();
state_ = STOPPED;
OnStop();
}
}
void RemoteCommandsInvalidator::OnInvalidatorStateChange(
invalidation::InvalidatorState state) {
DCHECK_EQ(STARTED, state_);
DCHECK(thread_checker_.CalledOnValidThread());
invalidation_service_enabled_ = state == invalidation::INVALIDATIONS_ENABLED;
UpdateInvalidationsEnabled();
}
void RemoteCommandsInvalidator::OnIncomingInvalidation(
const invalidation::TopicInvalidationMap& invalidation_map) {
DCHECK_EQ(STARTED, state_);
DCHECK(thread_checker_.CalledOnValidThread());
VLOG(2) << "Received remote command invalidation";
if (!invalidation_service_enabled_)
LOG(WARNING) << "Unexpected invalidation received.";
const invalidation::SingleTopicInvalidationSet& list =
invalidation_map.ForTopic(topic_);
if (list.IsEmpty()) {
NOTREACHED();
return;
}
// Acknowledge all invalidations.
for (const auto& it : list)
it.Acknowledge();
DoRemoteCommandsFetch(list.back());
}
std::string RemoteCommandsInvalidator::GetOwnerName() const {
return owner_name_;
}
bool RemoteCommandsInvalidator::IsPublicTopic(
const invalidation::Topic& topic) const {
return IsPublicInvalidationTopic(topic);
}
void RemoteCommandsInvalidator::ReloadPolicyData(
const enterprise_management::PolicyData* policy) {
DCHECK(thread_checker_.CalledOnValidThread());
if (state_ != STARTED)
return;
// Create the Topic based on the policy data.
// If the policy does not specify the Topic, then unsubscribe and unregister.
invalidation::Topic topic;
if (!policy || !GetRemoteCommandTopicFromPolicy(*policy, &topic)) {
UnsubscribeFromTopics();
Unregister();
return;
}
// If the policy topic in the policy data is different from the currently
// registered topic, update the object registration.
if (!is_registered_ || topic != topic_)
Register(topic);
}
void RemoteCommandsInvalidator::Register(const invalidation::Topic& topic) {
// Register this handler with the invalidation service if needed.
if (!is_registered_) {
OnInvalidatorStateChange(invalidation_service_->GetInvalidatorState());
invalidation_service_->RegisterInvalidationHandler(this);
is_registered_ = true;
}
topic_ = topic;
UpdateInvalidationsEnabled();
// Update subscription with the invalidation service.
const bool success =
invalidation_service_->UpdateInterestedTopics(this, /*topics=*/{topic});
base::UmaHistogramBoolean(kMetricRemoteCommandInvalidationsRegistrationResult,
success);
CHECK(success);
}
void RemoteCommandsInvalidator::Unregister() {
if (is_registered_) {
invalidation_service_->UnregisterInvalidationHandler(this);
is_registered_ = false;
UpdateInvalidationsEnabled();
}
}
void RemoteCommandsInvalidator::UnsubscribeFromTopics() {
base::ScopedObservation<invalidation::InvalidationService,
invalidation::InvalidationHandler>
temporary_registration(this);
// Invalidator cannot unset its topics without being registered. Let's quickly
// register and unregister to do just that.
if (!is_registered_)
temporary_registration.Observe(invalidation_service_);
CHECK(invalidation_service_->UpdateInterestedTopics(
this, invalidation::TopicSet()));
}
void RemoteCommandsInvalidator::UpdateInvalidationsEnabled() {
invalidations_enabled_ = invalidation_service_enabled_ && is_registered_;
}
} // namespace policy