Refactor ChromeOSPolicy into policy and utilities

Building on the policy fragments that have been previously extracted
from ChromeOSPolicy, this extracts a few more pieces of logic and
replaces sections of ChromeOSPolicy with calls to the extracted
methods.

Bug: 66016687
Test: unit tests, manually triggered OTA updates

Change-Id: I3bc608065f8ab89982f71b8490ebd66ed2266aa3
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
index 12f417c..842839a 100644
--- a/update_manager/chromeos_policy.cc
+++ b/update_manager/chromeos_policy.cc
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <set>
 #include <string>
+#include <vector>
 
 #include <base/logging.h>
 #include <base/strings/string_util.h>
@@ -28,6 +29,11 @@
 #include "update_engine/common/error_code_utils.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/update_manager/device_policy_provider.h"
+#include "update_engine/update_manager/enough_slots_ab_updates_policy_impl.h"
+#include "update_engine/update_manager/enterprise_device_policy_impl.h"
+#include "update_engine/update_manager/interactive_update_policy_impl.h"
+#include "update_engine/update_manager/official_build_check_policy_impl.h"
+#include "update_engine/update_manager/out_of_box_experience_policy_impl.h"
 #include "update_engine/update_manager/policy_utils.h"
 #include "update_engine/update_manager/shill_provider.h"
 
@@ -38,10 +44,10 @@
 using chromeos_update_engine::ErrorCode;
 using chromeos_update_engine::InstallPlan;
 using std::get;
-using std::max;
 using std::min;
 using std::set;
 using std::string;
+using std::vector;
 
 namespace {
 
@@ -172,21 +178,16 @@
 
 namespace chromeos_update_manager {
 
-const int ChromeOSPolicy::kTimeoutInitialInterval =  7 * 60;
+const NextUpdateCheckPolicyConstants
+    ChromeOSPolicy::kNextUpdateCheckPolicyConstants = {
+        .timeout_initial_interval = 7 * 60,
+        .timeout_periodic_interval = 45 * 60,
+        .timeout_max_backoff_interval = 4 * 60 * 60,
+        .timeout_regular_fuzz = 10 * 60,
+        .attempt_backoff_max_interval_in_days = 16,
+        .attempt_backoff_fuzz_in_hours = 12,
+};
 
-// TODO(deymo): Split the update_manager policies for Brillo and ChromeOS and
-// make the update check periodic interval configurable.
-#ifdef __ANDROID__
-const int ChromeOSPolicy::kTimeoutPeriodicInterval = 5 * 60 * 60;
-const int ChromeOSPolicy::kTimeoutMaxBackoffInterval = 26 * 60 * 60;
-#else
-const int ChromeOSPolicy::kTimeoutPeriodicInterval = 45 * 60;
-const int ChromeOSPolicy::kTimeoutMaxBackoffInterval = 4 * 60 * 60;
-#endif  // __ANDROID__
-
-const int ChromeOSPolicy::kTimeoutRegularFuzz = 10 * 60;
-const int ChromeOSPolicy::kAttemptBackoffMaxIntervalInDays = 16;
-const int ChromeOSPolicy::kAttemptBackoffFuzzInHours = 12;
 const int ChromeOSPolicy::kMaxP2PAttempts = 10;
 const int ChromeOSPolicy::kMaxP2PAttemptsPeriodInSeconds = 5 * 24 * 60 * 60;
 
@@ -199,130 +200,53 @@
   result->target_version_prefix.clear();
   result->is_interactive = false;
 
-  DevicePolicyProvider* const dp_provider = state->device_policy_provider();
-  UpdaterProvider* const updater_provider = state->updater_provider();
-  SystemProvider* const system_provider = state->system_provider();
+  EnoughSlotsAbUpdatesPolicyImpl enough_slots_ab_updates_policy;
+  EnterpriseDevicePolicyImpl enterprise_device_policy;
+  OnlyUpdateOfficialBuildsPolicyImpl only_update_official_builds_policy;
+  InteractiveUpdatePolicyImpl interactive_update_policy;
+  OobePolicyImpl oobe_policy;
+  NextUpdateCheckTimePolicyImpl next_update_check_time_policy(
+      kNextUpdateCheckPolicyConstants);
 
-  // Do not perform any updates if booted from removable device. This decision
-  // is final.
-  const unsigned int* num_slots_p = ec->GetValue(
-      system_provider->var_num_slots());
-  if (!num_slots_p || *num_slots_p < 2) {
-    LOG(INFO) << "Not enough slots for A/B updates, disabling update checks.";
-    result->updates_enabled = false;
+  vector<Policy const*> policies_to_consult = {
+      // Do not perform any updates if there are not enough slots to do A/B
+      // updates.
+      &enough_slots_ab_updates_policy,
+
+      // Check to see if Enterprise-managed (has DevicePolicy) and/or
+      // Kiosk-mode.  If so, then defer to those settings.
+      &enterprise_device_policy,
+
+      // Check to see if an interactive update was requested.
+      &interactive_update_policy,
+
+      // Unofficial builds should not perform periodic update checks.
+      &only_update_official_builds_policy,
+
+      // If OOBE is enabled, wait until it is completed.
+      &oobe_policy,
+
+      // Ensure that periodic update checks are timed properly.
+      &next_update_check_time_policy,
+  };
+
+  // Now that the list of policy implementations, and the order to consult them,
+  // has been setup, consult the policies. If none of the policies make a
+  // definitive decisions about whether or not to check for updates, then allow
+  // the update check to happen.
+  EvalStatus status = ConsultPolicies(policies_to_consult,
+                                      &Policy::UpdateCheckAllowed,
+                                      ec,
+                                      state,
+                                      error,
+                                      result);
+  if (EvalStatus::kContinue != status) {
+    return status;
+  } else {
+    // It is time to check for an update.
+    LOG(INFO) << "Allowing update check.";
     return EvalStatus::kSucceeded;
   }
-
-  const bool* device_policy_is_loaded_p = ec->GetValue(
-      dp_provider->var_device_policy_is_loaded());
-  if (device_policy_is_loaded_p && *device_policy_is_loaded_p) {
-    bool kiosk_app_control_chrome_version = false;
-
-    // Check whether updates are disabled by policy.
-    const bool* update_disabled_p = ec->GetValue(
-        dp_provider->var_update_disabled());
-    if (update_disabled_p && *update_disabled_p) {
-      // Check whether allow kiosk app to control chrome version policy. This
-      // policy is only effective when AU is disabled by admin.
-      const bool* allow_kiosk_app_control_chrome_version_p = ec->GetValue(
-          dp_provider->var_allow_kiosk_app_control_chrome_version());
-      kiosk_app_control_chrome_version =
-          allow_kiosk_app_control_chrome_version_p &&
-          *allow_kiosk_app_control_chrome_version_p;
-      if (!kiosk_app_control_chrome_version) {
-        // No kiosk pin chrome version policy. AU is really disabled.
-        LOG(INFO) << "Updates disabled by policy, blocking update checks.";
-        return EvalStatus::kAskMeAgainLater;
-      }
-    }
-
-    if (kiosk_app_control_chrome_version) {
-      // Get the required platform version from Chrome.
-      const string* kiosk_required_platform_version_p =
-          ec->GetValue(system_provider->var_kiosk_required_platform_version());
-      if (!kiosk_required_platform_version_p) {
-        LOG(INFO) << "Kiosk app required platform version is not fetched, "
-                     "blocking update checks";
-        return EvalStatus::kAskMeAgainLater;
-      }
-
-      result->target_version_prefix = *kiosk_required_platform_version_p;
-      LOG(INFO) << "Allow kiosk app to control Chrome version policy is set, "
-                << "target version is "
-                << (!kiosk_required_platform_version_p->empty()
-                        ? *kiosk_required_platform_version_p
-                        : std::string("latest"));
-    } else {
-      // Determine whether a target version prefix is dictated by policy.
-      const string* target_version_prefix_p = ec->GetValue(
-          dp_provider->var_target_version_prefix());
-      if (target_version_prefix_p)
-        result->target_version_prefix = *target_version_prefix_p;
-    }
-
-    // Determine whether a target channel is dictated by policy.
-    const bool* release_channel_delegated_p = ec->GetValue(
-        dp_provider->var_release_channel_delegated());
-    if (release_channel_delegated_p && !(*release_channel_delegated_p)) {
-      const string* release_channel_p = ec->GetValue(
-          dp_provider->var_release_channel());
-      if (release_channel_p)
-        result->target_channel = *release_channel_p;
-    }
-  }
-
-  // First, check to see if an interactive update was requested.
-  const UpdateRequestStatus* forced_update_requested_p = ec->GetValue(
-      updater_provider->var_forced_update_requested());
-  if (forced_update_requested_p &&
-      *forced_update_requested_p != UpdateRequestStatus::kNone) {
-    result->is_interactive =
-        (*forced_update_requested_p == UpdateRequestStatus::kInteractive);
-    LOG(INFO) << "Forced update signaled ("
-              << (result->is_interactive ?  "interactive" : "periodic")
-              << "), allowing update check.";
-    return EvalStatus::kSucceeded;
-  }
-
-  // The logic thereafter applies to periodic updates. Bear in mind that we
-  // should not return a final "no" if any of these criteria are not satisfied,
-  // because the system may still update due to an interactive update request.
-
-  // Unofficial builds should not perform periodic update checks.
-  const bool* is_official_build_p = ec->GetValue(
-      system_provider->var_is_official_build());
-  if (is_official_build_p && !(*is_official_build_p)) {
-    LOG(INFO) << "Unofficial build, blocking periodic update checks.";
-    return EvalStatus::kAskMeAgainLater;
-  }
-
-  // If OOBE is enabled, wait until it is completed.
-  const bool* is_oobe_enabled_p = ec->GetValue(
-      state->config_provider()->var_is_oobe_enabled());
-  if (is_oobe_enabled_p && *is_oobe_enabled_p) {
-    const bool* is_oobe_complete_p = ec->GetValue(
-        system_provider->var_is_oobe_complete());
-    if (is_oobe_complete_p && !(*is_oobe_complete_p)) {
-      LOG(INFO) << "OOBE not completed, blocking update checks.";
-      return EvalStatus::kAskMeAgainLater;
-    }
-  }
-
-  // Ensure that periodic update checks are timed properly.
-  Time next_update_check;
-  if (NextUpdateCheckTime(ec, state, error, &next_update_check) !=
-      EvalStatus::kSucceeded) {
-    return EvalStatus::kFailed;
-  }
-  if (!ec->IsWallclockTimeGreaterThan(next_update_check)) {
-    LOG(INFO) << "Periodic check interval not satisfied, blocking until "
-              << chromeos_update_engine::utils::ToString(next_update_check);
-    return EvalStatus::kAskMeAgainLater;
-  }
-
-  // It is time to check for an update.
-  LOG(INFO) << "Allowing update check.";
-  return EvalStatus::kSucceeded;
 }
 
 EvalStatus ChromeOSPolicy::UpdateCanBeApplied(EvaluationContext* ec,
@@ -626,92 +550,6 @@
   return status;
 }
 
-EvalStatus ChromeOSPolicy::NextUpdateCheckTime(EvaluationContext* ec,
-                                               State* state, string* error,
-                                               Time* next_update_check) const {
-  UpdaterProvider* const updater_provider = state->updater_provider();
-
-  // Don't check for updates too often. We limit the update checks to once every
-  // some interval. The interval is kTimeoutInitialInterval the first time and
-  // kTimeoutPeriodicInterval for the subsequent update checks. If the update
-  // check fails, we increase the interval between the update checks
-  // exponentially until kTimeoutMaxBackoffInterval. Finally, to avoid having
-  // many chromebooks running update checks at the exact same time, we add some
-  // fuzz to the interval.
-  const Time* updater_started_time =
-      ec->GetValue(updater_provider->var_updater_started_time());
-  POLICY_CHECK_VALUE_AND_FAIL(updater_started_time, error);
-
-  const Time* last_checked_time =
-      ec->GetValue(updater_provider->var_last_checked_time());
-
-  const uint64_t* seed = ec->GetValue(state->random_provider()->var_seed());
-  POLICY_CHECK_VALUE_AND_FAIL(seed, error);
-
-  PRNG prng(*seed);
-
-  // If this is the first attempt, compute and return an initial value.
-  if (!last_checked_time || *last_checked_time < *updater_started_time) {
-    *next_update_check = *updater_started_time + FuzzedInterval(
-        &prng, kTimeoutInitialInterval, kTimeoutRegularFuzz);
-    return EvalStatus::kSucceeded;
-  }
-
-  // Check whether the server is enforcing a poll interval; if not, this value
-  // will be zero.
-  const unsigned int* server_dictated_poll_interval = ec->GetValue(
-      updater_provider->var_server_dictated_poll_interval());
-  POLICY_CHECK_VALUE_AND_FAIL(server_dictated_poll_interval, error);
-
-  int interval = *server_dictated_poll_interval;
-  int fuzz = 0;
-
-  // If no poll interval was dictated by server compute a back-off period,
-  // starting from a predetermined base periodic interval and increasing
-  // exponentially by the number of consecutive failed attempts.
-  if (interval == 0) {
-    const unsigned int* consecutive_failed_update_checks = ec->GetValue(
-        updater_provider->var_consecutive_failed_update_checks());
-    POLICY_CHECK_VALUE_AND_FAIL(consecutive_failed_update_checks, error);
-
-    interval = kTimeoutPeriodicInterval;
-    unsigned int num_failures = *consecutive_failed_update_checks;
-    while (interval < kTimeoutMaxBackoffInterval && num_failures) {
-      interval *= 2;
-      num_failures--;
-    }
-  }
-
-  // We cannot back off longer than the predetermined maximum interval.
-  if (interval > kTimeoutMaxBackoffInterval)
-    interval = kTimeoutMaxBackoffInterval;
-
-  // We cannot back off shorter than the predetermined periodic interval. Also,
-  // in this case set the fuzz to a predetermined regular value.
-  if (interval <= kTimeoutPeriodicInterval) {
-    interval = kTimeoutPeriodicInterval;
-    fuzz = kTimeoutRegularFuzz;
-  }
-
-  // If not otherwise determined, defer to a fuzz of +/-(interval / 2).
-  if (fuzz == 0)
-    fuzz = interval;
-
-  *next_update_check = *last_checked_time + FuzzedInterval(
-      &prng, interval, fuzz);
-  return EvalStatus::kSucceeded;
-}
-
-TimeDelta ChromeOSPolicy::FuzzedInterval(PRNG* prng, int interval, int fuzz) {
-  DCHECK_GE(interval, 0);
-  DCHECK_GE(fuzz, 0);
-  int half_fuzz = fuzz / 2;
-  // This guarantees the output interval is non negative.
-  int interval_min = max(interval - half_fuzz, 0);
-  int interval_max = interval + half_fuzz;
-  return TimeDelta::FromSeconds(prng->RandMinMax(interval_min, interval_max));
-}
-
 EvalStatus ChromeOSPolicy::UpdateBackoffAndDownloadUrl(
     EvaluationContext* ec, State* state, string* error,
     UpdateBackoffAndDownloadUrlResult* result,
@@ -883,11 +721,13 @@
     PRNG prng(*seed);
     int exp = min(update_state.num_failures,
                        static_cast<int>(sizeof(int)) * 8 - 2);
-    TimeDelta backoff_interval = TimeDelta::FromDays(
-        min(1 << exp, kAttemptBackoffMaxIntervalInDays));
-    TimeDelta backoff_fuzz = TimeDelta::FromHours(kAttemptBackoffFuzzInHours);
-    TimeDelta wait_period = FuzzedInterval(&prng, backoff_interval.InSeconds(),
-                                           backoff_fuzz.InSeconds());
+    TimeDelta backoff_interval = TimeDelta::FromDays(min(
+        1 << exp,
+        kNextUpdateCheckPolicyConstants.attempt_backoff_max_interval_in_days));
+    TimeDelta backoff_fuzz = TimeDelta::FromHours(
+        kNextUpdateCheckPolicyConstants.attempt_backoff_fuzz_in_hours);
+    TimeDelta wait_period = NextUpdateCheckTimePolicyImpl::FuzzedInterval(
+        &prng, backoff_interval.InSeconds(), backoff_fuzz.InSeconds());
     backoff_expiry = err_time + wait_period;
 
     // If the newly computed backoff already expired, nullify it.