Index: chrome/browser/policy/cloud/cloud_policy_invalidator.cc |
diff --git a/chrome/browser/policy/cloud/cloud_policy_invalidator.cc b/chrome/browser/policy/cloud/cloud_policy_invalidator.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b1c2e3dfa5359bc44bbd43c4a8259b1a3932982a |
--- /dev/null |
+++ b/chrome/browser/policy/cloud/cloud_policy_invalidator.cc |
@@ -0,0 +1,286 @@ |
+// Copyright (c) 2013 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/policy/cloud/cloud_policy_invalidator.h" |
+ |
+#include "base/bind.h" |
+#include "base/command_line.h" |
+#include "base/message_loop/message_loop.h" |
Joao da Silva
2013/07/25 18:03:53
not used
Steve Condie
2013/07/26 01:39:25
Done.
|
+#include "base/metrics/histogram.h" |
+#include "base/rand_util.h" |
+#include "base/sequenced_task_runner.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/time/time.h" |
+#include "base/values.h" |
+#include "chrome/browser/invalidation/invalidation_service.h" |
+#include "chrome/browser/policy/cloud/cloud_policy_client.h" |
+#include "chrome/browser/policy/cloud/cloud_policy_store.h" |
Joao da Silva
2013/07/25 18:03:53
alread included in the header
Steve Condie
2013/07/26 01:39:25
Done.
|
+#include "chrome/browser/policy/cloud/enterprise_metrics.h" |
+#include "chrome/common/chrome_switches.h" |
+#include "policy/policy_constants.h" |
+#include "sync/notifier/object_id_invalidation_map.h" |
+ |
+namespace policy { |
+ |
+const int CloudPolicyInvalidator::kMissingPayloadDelay = 5; |
+const int CloudPolicyInvalidator::kMaxFetchDelayDefault = 5000; |
+const int CloudPolicyInvalidator::kMaxFetchDelayMin = 1000; |
+const int CloudPolicyInvalidator::kMaxFetchDelayMax = 300000; |
+ |
+CloudPolicyInvalidator::CloudPolicyInvalidator( |
+ invalidation::InvalidationService* invalidation_service, |
+ CloudPolicyClient* client, |
+ CloudPolicyStore* store, |
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner, |
+ CloudPolicyInvalidationHandler* invalidation_handler) |
+ : invalidation_service_(invalidation_service), |
+ client_(client), |
+ store_(store), |
+ task_runner_(task_runner), |
+ invalidation_handler_(invalidation_handler), |
+ invalidations_enabled_(false), |
+ invalidation_service_enabled_(false), |
+ registered_timestamp_(0), |
+ invalid_(false), |
+ invalidation_version_(0), |
+ unknown_version_invalidation_count_(0), |
+ ack_handle_(syncer::AckHandle::InvalidAckHandle()), |
+ weak_factory_(this), |
+ max_fetch_delay_(kMaxFetchDelayDefault) { |
+ DCHECK(invalidation_service); |
+ DCHECK(client); |
+ DCHECK(store); |
+ DCHECK(task_runner.get()); |
+ DCHECK(invalidation_handler); |
+ |
+ OnInvalidatorStateChange(invalidation_service->GetInvalidatorState()); |
+ invalidation_service->RegisterInvalidationHandler(this); |
+ OnStoreLoaded(store); |
+ store->AddObserver(this); |
+} |
+ |
+CloudPolicyInvalidator::~CloudPolicyInvalidator() { |
+ invalidation_service_->UnregisterInvalidationHandler(this); |
+ store_->RemoveObserver(this); |
+} |
+ |
+void CloudPolicyInvalidator::Unregister() { |
+ if (invalid_) |
+ AcknowledgeInvalidation(); |
+ invalidation_service_->UpdateRegisteredInvalidationIds( |
+ this, |
+ syncer::ObjectIdSet()); |
+ registered_timestamp_ = 0; |
+ UpdateInvalidationsEnabled(); |
+} |
+ |
+void CloudPolicyInvalidator::OnInvalidatorStateChange( |
+ syncer::InvalidatorState state) { |
+ invalidation_service_enabled_ = state == syncer::INVALIDATIONS_ENABLED; |
+ UpdateInvalidationsEnabled(); |
+} |
+ |
+void CloudPolicyInvalidator::OnIncomingInvalidation( |
+ const syncer::ObjectIdInvalidationMap& invalidation_map) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ const syncer::ObjectIdInvalidationMap::const_iterator invalidation = |
+ invalidation_map.find(object_id_); |
+ if (invalidation == invalidation_map.end()) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ HandleInvalidation(invalidation->second); |
+} |
+ |
+void CloudPolicyInvalidator::OnStoreLoaded(CloudPolicyStore* store) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (registered_timestamp_) { |
+ // Update the kMetricPolicyRefresh histogram. In some cases, this object can |
+ // be constructed during an OnStoreLoaded callback, which causes |
+ // OnStoreLoaded to be called twice at initialization time, so make sure |
+ // that the timestamp does not match the timestamp at which registration |
+ // occurred. We only measure changes which occur after registration. |
+ if (!store->policy() || !store->policy()->has_timestamp() || |
+ store->policy()->timestamp() != registered_timestamp_) { |
+ UMA_HISTOGRAM_ENUMERATION( |
+ kMetricPolicyRefresh, |
+ GetPolicyRefreshMetric(), |
+ kMetricPolicyRefreshSize); |
+ } |
+ |
+ // If the policy was invalid and the version stored matches the latest |
+ // invalidation version, acknowledge the latest invalidation. |
+ if (invalid_ && store->invalidation_version() == invalidation_version_) |
+ AcknowledgeInvalidation(); |
+ } |
+ |
+ UpdateRegistration(store->policy()); |
+ UpdateMaxFetchDelay(store->policy_map()); |
+} |
+ |
+void CloudPolicyInvalidator::OnStoreError(CloudPolicyStore* store) {} |
+ |
+void CloudPolicyInvalidator::HandleInvalidation( |
+ const syncer::Invalidation& invalidation) { |
+ // The invalidation service may send an invalidation more than once if there |
+ // is a delay in acknowledging it. Duplicate invalidations are ignored. |
+ if (invalid_ && ack_handle_.Equals(invalidation.ack_handle)) |
+ return; |
+ |
+ // If there is still a pending invalidation, acknowledge it, since we only |
+ // care about the latest invalidation. |
+ if (invalid_) |
+ AcknowledgeInvalidation(); |
+ |
+ // Update invalidation state. |
+ invalid_ = true; |
+ ack_handle_ = invalidation.ack_handle; |
+ invalidation_version_ = invalidation.version; |
+ |
+ // When an invalidation with unknown version is received, use negative |
+ // numbers based on the number of such invalidations received. This |
+ // ensures that the version numbers do not collide with "real" versions |
+ // (which are positive) or previous invalidations with unknown version. |
+ if (invalidation_version_ == syncer::Invalidation::kUnknownVersion) |
+ invalidation_version_ = -(++unknown_version_invalidation_count_); |
+ |
+ // In order to prevent the cloud policy server from becoming overwhelmed when |
+ // a policy with many users is modified, delay for a random period of time |
+ // before fetching the policy. Delay for at least 20ms so that if multiple |
+ // invalidations are received in quick succession, only one fetch will be |
+ // performed. |
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds( |
+ base::RandInt(20, max_fetch_delay_)); |
+ |
+ // If there is a payload, the invalidate callback can run at any time, so set |
+ // the version and payload on the client immediately. Otherwise, the callback |
+ // must only run after at least kMissingPayloadDelay minutes. |
+ const std::string& payload = invalidation.payload; |
+ if (!invalidation.payload.empty()) |
+ client_->SetInvalidationInfo(invalidation_version_, payload); |
+ else |
+ delay += base::TimeDelta::FromMinutes(kMissingPayloadDelay); |
+ |
+ // Schedule the invalidate callback to run. |
+ task_runner_->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind( |
+ &CloudPolicyInvalidator::RunInvalidateCallback, |
+ weak_factory_.GetWeakPtr(), |
+ payload.empty() /* is_missing_payload */), |
+ delay); |
+ |
+ // Update the kMetricPolicyInvalidations histogram. |
+ UMA_HISTOGRAM_ENUMERATION( |
+ kMetricPolicyInvalidations, |
+ payload.empty() ? |
+ kMetricPolicyInvalidationsNoPayload : |
+ kMetricPolicyInvalidationsPayload, |
+ kMetricPolicyInvalidationsSize); |
+} |
+ |
+void CloudPolicyInvalidator::UpdateRegistration( |
+ const enterprise_management::PolicyData* policy) { |
+ // Create the ObjectId based on the policy data. |
+ // If the policy does not specify an the ObjectId, then unregister. |
+ if (!policy || |
+ !policy->has_timestamp() || |
+ !policy->has_invalidation_source() || |
+ !policy->has_invalidation_name()) { |
+ if (registered_timestamp_) |
+ Unregister(); |
+ return; |
+ } |
+ invalidation::ObjectId object_id( |
+ policy->invalidation_source(), |
+ policy->invalidation_name()); |
+ |
+ // If the policy object id in the policy data is different from the currently |
+ // registered object id, update the object registration. |
+ if (!registered_timestamp_ || !(object_id == object_id_)) { |
+ if (invalid_) |
+ AcknowledgeInvalidation(); |
+ registered_timestamp_ = policy->timestamp(); |
+ object_id_ = object_id; |
+ UpdateInvalidationsEnabled(); |
+ |
+ syncer::ObjectIdSet ids; |
+ ids.insert(object_id); |
+ invalidation_service_->UpdateRegisteredInvalidationIds(this, ids); |
+ } |
+} |
+ |
+void CloudPolicyInvalidator::UpdateMaxFetchDelay(const PolicyMap& policy_map) { |
+ int delay; |
+ |
+ // Try reading the delay from the policy. |
+ const base::Value* delay_policy_value = |
+ policy_map.GetValue(key::kMaxInvalidationFetchDelay); |
+ if (delay_policy_value && delay_policy_value->GetAsInteger(&delay)) { |
+ set_max_fetch_delay(delay); |
+ return; |
+ } |
+ |
+ // Try reading the delay from the command line switch. |
+ std::string delay_string = |
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
+ switches::kCloudPolicyInvalidationDelay); |
+ if (base::StringToInt(delay_string, &delay)) { |
+ set_max_fetch_delay(delay); |
+ return; |
+ } |
+ |
+ max_fetch_delay_ = kMaxFetchDelayDefault; |
Joao da Silva
2013/07/25 18:03:53
For consistency:
set_max_fetch_delay(kMaxFetchD
Steve Condie
2013/07/26 01:39:25
Done.
|
+} |
+ |
+void CloudPolicyInvalidator::set_max_fetch_delay(int delay) { |
+ if (delay < kMaxFetchDelayMin) |
+ max_fetch_delay_ = kMaxFetchDelayMin; |
+ else if (delay > kMaxFetchDelayMax) |
+ max_fetch_delay_ = kMaxFetchDelayMax; |
+ else |
+ max_fetch_delay_ = delay; |
+} |
+ |
+void CloudPolicyInvalidator::UpdateInvalidationsEnabled() { |
+ bool invalidations_enabled = |
+ invalidation_service_enabled_ && registered_timestamp_; |
+ if (invalidations_enabled_ != invalidations_enabled) { |
+ invalidations_enabled_ = invalidations_enabled; |
+ invalidation_handler_->OnInvalidatorStateChanged(invalidations_enabled); |
+ } |
+} |
+ |
+void CloudPolicyInvalidator::RunInvalidateCallback(bool is_missing_payload) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ // In the missing payload case, the invalidation version has not been set on |
+ // the client yet, so set it now that the required time has elapsed. |
+ if (is_missing_payload) |
+ client_->SetInvalidationInfo(invalidation_version_, std::string()); |
+ invalidation_handler_->InvalidatePolicy(); |
+} |
+ |
+void CloudPolicyInvalidator::AcknowledgeInvalidation() { |
+ DCHECK(invalid_); |
+ invalid_ = false; |
+ client_->SetInvalidationInfo(0, std::string()); |
+ invalidation_service_->AcknowledgeInvalidation(object_id_, ack_handle_); |
+ // Cancel any scheduled invalidate callbacks. |
+ weak_factory_.InvalidateWeakPtrs(); |
+} |
+ |
+int CloudPolicyInvalidator::GetPolicyRefreshMetric() { |
+ if (store_->policy_changed()) { |
+ if (invalid_) |
+ return kMetricPolicyRefreshInvalidatedChanged; |
+ if (invalidations_enabled_) |
+ return kMetricPolicyRefreshChanged; |
+ return kMetricPolicyRefreshChangedNoInvalidations; |
+ } |
+ if (invalid_) |
+ return kMetricPolicyRefreshInvalidatedUnchanged; |
+ return kMetricPolicyRefreshUnchanged; |
+} |
+ |
+} // namespace policy |