| // |
| // Copyright (C) 2014 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #ifndef UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_ |
| #define UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_ |
| |
| #include <memory> |
| #include <string> |
| |
| #include <base/bind.h> |
| #include <base/location.h> |
| #include <brillo/message_loops/message_loop.h> |
| |
| #include "update_engine/update_manager/evaluation_context.h" |
| |
| namespace chromeos_update_manager { |
| |
| template<typename R, typename... Args> |
| EvalStatus UpdateManager::EvaluatePolicy( |
| EvaluationContext* ec, |
| EvalStatus (Policy::*policy_method)(EvaluationContext*, State*, |
| std::string*, R*, |
| Args...) const, |
| R* result, Args... args) { |
| // If expiration timeout fired, dump the context and reset expiration. |
| // IMPORTANT: We must still proceed with evaluation of the policy in this |
| // case, so that the evaluation time (and corresponding reevaluation timeouts) |
| // are readjusted. |
| if (ec->is_expired()) { |
| LOG(WARNING) << "Request timed out, evaluation context: " |
| << ec->DumpContext(); |
| ec->ResetExpiration(); |
| } |
| |
| // Reset the evaluation context. |
| ec->ResetEvaluation(); |
| |
| const std::string policy_name = policy_->PolicyRequestName(policy_method); |
| LOG(INFO) << policy_name << ": START"; |
| |
| // First try calling the actual policy. |
| std::string error; |
| EvalStatus status = (policy_.get()->*policy_method)(ec, state_.get(), &error, |
| result, args...); |
| // If evaluating the main policy failed, defer to the default policy. |
| if (status == EvalStatus::kFailed) { |
| LOG(WARNING) << "Evaluating policy failed: " << error |
| << "\nEvaluation context: " << ec->DumpContext(); |
| error.clear(); |
| status = (default_policy_.*policy_method)(ec, state_.get(), &error, result, |
| args...); |
| if (status == EvalStatus::kFailed) { |
| LOG(WARNING) << "Evaluating default policy failed: " << error; |
| } else if (status == EvalStatus::kAskMeAgainLater) { |
| LOG(ERROR) |
| << "Default policy would block; this is a bug, forcing failure."; |
| status = EvalStatus::kFailed; |
| } |
| } |
| |
| LOG(INFO) << policy_name << ": END"; |
| |
| return status; |
| } |
| |
| template<typename R, typename... Args> |
| void UpdateManager::OnPolicyReadyToEvaluate( |
| scoped_refptr<EvaluationContext> ec, |
| base::Callback<void(EvalStatus status, const R& result)> callback, |
| EvalStatus (Policy::*policy_method)(EvaluationContext*, State*, |
| std::string*, R*, |
| Args...) const, |
| Args... args) { |
| // Evaluate the policy. |
| R result; |
| EvalStatus status = EvaluatePolicy(ec.get(), policy_method, &result, args...); |
| |
| if (status != EvalStatus::kAskMeAgainLater) { |
| // AsyncPolicyRequest finished. |
| callback.Run(status, result); |
| return; |
| } |
| |
| // Re-schedule the policy request based on used variables. |
| base::Closure reeval_callback = base::Bind( |
| &UpdateManager::OnPolicyReadyToEvaluate<R, Args...>, |
| base::Unretained(this), ec, callback, |
| policy_method, args...); |
| if (ec->RunOnValueChangeOrTimeout(reeval_callback)) |
| return; // Reevaluation scheduled successfully. |
| |
| // Scheduling a reevaluation can fail because policy method didn't use any |
| // non-const variable nor there's any time-based event that will change the |
| // status of evaluation. Alternatively, this may indicate an error in the use |
| // of the scheduling interface. |
| LOG(ERROR) << "Failed to schedule a reevaluation of policy " |
| << policy_->PolicyRequestName(policy_method) << "; this is a bug."; |
| callback.Run(status, result); |
| } |
| |
| template<typename R, typename... ActualArgs, typename... ExpectedArgs> |
| EvalStatus UpdateManager::PolicyRequest( |
| EvalStatus (Policy::*policy_method)(EvaluationContext*, State*, |
| std::string*, R*, |
| ExpectedArgs...) const, |
| R* result, ActualArgs... args) { |
| scoped_refptr<EvaluationContext> ec( |
| new EvaluationContext(clock_, evaluation_timeout_)); |
| // A PolicyRequest always consists on a single evaluation on a new |
| // EvaluationContext. |
| // IMPORTANT: To ensure that ActualArgs can be converted to ExpectedArgs, we |
| // explicitly instantiate EvaluatePolicy with the latter in lieu of the |
| // former. |
| EvalStatus ret = EvaluatePolicy<R, ExpectedArgs...>(ec.get(), policy_method, |
| result, args...); |
| // Sync policy requests must not block, if they do then this is an error. |
| DCHECK(EvalStatus::kAskMeAgainLater != ret); |
| LOG_IF(WARNING, EvalStatus::kAskMeAgainLater == ret) |
| << "Sync request used with an async policy; this is a bug"; |
| return ret; |
| } |
| |
| template<typename R, typename... ActualArgs, typename... ExpectedArgs> |
| void UpdateManager::AsyncPolicyRequest( |
| base::Callback<void(EvalStatus, const R& result)> callback, |
| EvalStatus (Policy::*policy_method)(EvaluationContext*, State*, |
| std::string*, R*, |
| ExpectedArgs...) const, |
| ActualArgs... args) { |
| scoped_refptr<EvaluationContext> ec = |
| new EvaluationContext( |
| clock_, evaluation_timeout_, expiration_timeout_, |
| std::unique_ptr<base::Callback<void(EvaluationContext*)>>( |
| new base::Callback<void(EvaluationContext*)>( |
| base::Bind(&UpdateManager::UnregisterEvalContext, |
| weak_ptr_factory_.GetWeakPtr())))); |
| if (!ec_repo_.insert(ec.get()).second) { |
| LOG(ERROR) << "Failed to register evaluation context; this is a bug."; |
| } |
| |
| // IMPORTANT: To ensure that ActualArgs can be converted to ExpectedArgs, we |
| // explicitly instantiate UpdateManager::OnPolicyReadyToEvaluate with the |
| // latter in lieu of the former. |
| base::Closure eval_callback = base::Bind( |
| &UpdateManager::OnPolicyReadyToEvaluate<R, ExpectedArgs...>, |
| base::Unretained(this), ec, callback, policy_method, args...); |
| brillo::MessageLoop::current()->PostTask(FROM_HERE, eval_callback); |
| } |
| |
| } // namespace chromeos_update_manager |
| |
| #endif // UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_MANAGER_INL_H_ |