update_engine: Allow rollback to kiosk-specified target version

The RollbackToTargetVersion policy can now allow rolling back to
the target version specified by the kiosk app manifest if the
AllowKioskAppControlChromeVersion is set to true.

To use this, the following policies must be set:
- AutoUpdateDisabled = True
- AllowKioskAppControlChromeVersion = True
- RollbackToTargetVersion = ROLLBACK_WITH_FULL_POWERWASH
- TargetVersionPrefix = not set

And the kiosk app has to specify in the manifest.json:
- required_platform_version = 10718

This setup will now initiate a rollback.

BUG=chromium:853161
TEST='cros_run_unit_tests --board=caroline --packages update_engine'

Change-Id: I870e2e2ddc1c915e5599b28944a07ff55e711d5b
Reviewed-on: https://chromium-review.googlesource.com/1102509
Commit-Ready: Marton Hunyady <hunyadym@chromium.org>
Tested-by: Marton Hunyady <hunyadym@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
index bed4d53..524779f 100644
--- a/update_manager/chromeos_policy_unittest.cc
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -105,6 +105,43 @@
       curr_time -= TimeDelta::FromSeconds(1);
     fake_clock_.SetWallclockTime(curr_time);
   }
+
+  // Sets the policies required for a kiosk app to control Chrome OS version:
+  // - AllowKioskAppControlChromeVersion = True
+  // - UpdateDisabled = True
+  // In the kiosk app manifest:
+  // - RequiredPlatformVersion = 1234.
+  void SetKioskAppControlsChromeOsVersion() {
+    fake_state_.device_policy_provider()
+        ->var_allow_kiosk_app_control_chrome_version()
+        ->reset(new bool(true));
+    fake_state_.device_policy_provider()->var_update_disabled()->reset(
+        new bool(true));
+    fake_state_.system_provider()->var_kiosk_required_platform_version()->reset(
+        new string("1234."));
+  }
+
+  // Sets up a test with the value of RollbackToTargetVersion policy (and
+  // whether it's set), and returns the value of
+  // UpdateCheckParams.rollback_allowed.
+  bool TestRollbackAllowed(bool set_policy,
+                           RollbackToTargetVersion rollback_to_target_version) {
+    // Update check is allowed, response includes attributes for use in the
+    // request.
+    SetUpdateCheckAllowed(true);
+
+    if (set_policy) {
+      // Override RollbackToTargetVersion device policy attribute.
+      fake_state_.device_policy_provider()
+          ->var_rollback_to_target_version()
+          ->reset(new RollbackToTargetVersion(rollback_to_target_version));
+    }
+
+    UpdateCheckParams result;
+    ExpectPolicyStatus(
+        EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+    return result.rollback_allowed;
+  }
 };
 
 TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedWaitsForTheTimeout) {
@@ -208,62 +245,49 @@
 }
 
 TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackAllowed) {
-  // Update check is allowed, response includes attributes for use in the
-  // request.
-  SetUpdateCheckAllowed(true);
-
-  // Override RollbackToTargetVersion device policy attribute.
-  fake_state_.device_policy_provider()->var_rollback_to_target_version()->reset(
-      new RollbackToTargetVersion(
-          RollbackToTargetVersion::kRollbackWithFullPowerwash));
-
-  UpdateCheckParams result;
-  ExpectPolicyStatus(
-      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
-  EXPECT_TRUE(result.rollback_allowed);
+  EXPECT_TRUE(TestRollbackAllowed(
+      true, RollbackToTargetVersion::kRollbackWithFullPowerwash));
 }
 
 TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackDisabled) {
-  // Update check is allowed, response includes attributes for use in the
-  // request.
-  SetUpdateCheckAllowed(true);
-
-  // Override RollbackToTargetVersion device policy attribute.
-  fake_state_.device_policy_provider()->var_rollback_to_target_version()->reset(
-      new RollbackToTargetVersion(RollbackToTargetVersion::kDisabled));
-
-  UpdateCheckParams result;
-  ExpectPolicyStatus(
-      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
-  EXPECT_FALSE(result.rollback_allowed);
+  EXPECT_FALSE(TestRollbackAllowed(true, RollbackToTargetVersion::kDisabled));
 }
 
 TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackUnspecified) {
-  // Update check is allowed, response includes attributes for use in the
-  // request.
-  SetUpdateCheckAllowed(true);
-
-  // Override RollbackToTargetVersion device policy attribute.
-  fake_state_.device_policy_provider()->var_rollback_to_target_version()->reset(
-      new RollbackToTargetVersion(RollbackToTargetVersion::kUnspecified));
-
-  UpdateCheckParams result;
-  ExpectPolicyStatus(
-      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
-  EXPECT_FALSE(result.rollback_allowed);
+  EXPECT_FALSE(
+      TestRollbackAllowed(true, RollbackToTargetVersion::kUnspecified));
 }
 
 TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackNotSet) {
-  // Update check is allowed, response includes attributes for use in the
-  // request.
-  SetUpdateCheckAllowed(true);
+  EXPECT_FALSE(
+      TestRollbackAllowed(false, RollbackToTargetVersion::kUnspecified));
+}
 
-  // Don't set RollbackToTargetVersion device policy attribute.
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedKioskRollbackAllowed) {
+  SetKioskAppControlsChromeOsVersion();
 
-  UpdateCheckParams result;
-  ExpectPolicyStatus(
-      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
-  EXPECT_FALSE(result.rollback_allowed);
+  EXPECT_TRUE(TestRollbackAllowed(
+      true, RollbackToTargetVersion::kRollbackWithFullPowerwash));
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedKioskRollbackDisabled) {
+  SetKioskAppControlsChromeOsVersion();
+
+  EXPECT_FALSE(TestRollbackAllowed(true, RollbackToTargetVersion::kDisabled));
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedKioskRollbackUnspecified) {
+  SetKioskAppControlsChromeOsVersion();
+
+  EXPECT_FALSE(
+      TestRollbackAllowed(true, RollbackToTargetVersion::kUnspecified));
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedKioskRollbackNotSet) {
+  SetKioskAppControlsChromeOsVersion();
+
+  EXPECT_FALSE(
+      TestRollbackAllowed(false, RollbackToTargetVersion::kUnspecified));
 }
 
 TEST_F(UmChromeOSPolicyTest,
@@ -341,21 +365,13 @@
   // Update check is allowed.
   SetUpdateCheckAllowed(true);
 
-  // A typical setup for kiosk pin policy: AU disabled, allow kiosk to pin
-  // and there is a kiosk required platform version.
-  fake_state_.device_policy_provider()->var_update_disabled()->reset(
-      new bool(true));
-  fake_state_.device_policy_provider()
-      ->var_allow_kiosk_app_control_chrome_version()
-      ->reset(new bool(true));
-  fake_state_.system_provider()->var_kiosk_required_platform_version()->reset(
-      new string("1234.0.0"));
+  SetKioskAppControlsChromeOsVersion();
 
   UpdateCheckParams result;
   ExpectPolicyStatus(EvalStatus::kSucceeded,
                      &Policy::UpdateCheckAllowed, &result);
   EXPECT_TRUE(result.updates_enabled);
-  EXPECT_EQ("1234.0.0", result.target_version_prefix);
+  EXPECT_EQ("1234.", result.target_version_prefix);
   EXPECT_FALSE(result.interactive);
 }
 
diff --git a/update_manager/enterprise_device_policy_impl.cc b/update_manager/enterprise_device_policy_impl.cc
index e2a0c87..6f14b1f 100644
--- a/update_manager/enterprise_device_policy_impl.cc
+++ b/update_manager/enterprise_device_policy_impl.cc
@@ -55,6 +55,7 @@
       }
     }
 
+    // By default, result->rollback_allowed is false.
     if (kiosk_app_control_chrome_version) {
       // Get the required platform version from Chrome.
       const string* kiosk_required_platform_version_p =
@@ -68,30 +69,37 @@
       result->target_version_prefix = *kiosk_required_platform_version_p;
       LOG(INFO) << "Allow kiosk app to control Chrome version policy is set, "
                 << "target version is " << result->target_version_prefix;
-      // TODO(hunyadym): Add support of rollback for kiosk apps.
+      // TODO(hunyadym): Add support for allowing rollback using the manifest
+      // (if policy doesn't specify otherwise).
     } 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;
+    }
 
-      const RollbackToTargetVersion* rollback_to_target_version_p =
-          ec->GetValue(dp_provider->var_rollback_to_target_version());
-      if (rollback_to_target_version_p) {
-        switch (*rollback_to_target_version_p) {
-          case RollbackToTargetVersion::kUnspecified:
-          case RollbackToTargetVersion::kDisabled:
-            result->rollback_allowed = false;
-            break;
-          case RollbackToTargetVersion::kRollbackWithFullPowerwash:
-            result->rollback_allowed = true;
-            break;
-          case RollbackToTargetVersion::kMaxValue:
-            NOTREACHED();
-            // Don't add a default case to let the compiler warn about newly
-            // added enum values which should be added here.
-        }
+    // Policy always overwrites whether rollback is allowed by the kiosk app
+    // manifest.
+    const RollbackToTargetVersion* rollback_to_target_version_p =
+        ec->GetValue(dp_provider->var_rollback_to_target_version());
+    if (rollback_to_target_version_p) {
+      switch (*rollback_to_target_version_p) {
+        case RollbackToTargetVersion::kUnspecified:
+          // We leave the default or the one specified by the kiosk app.
+          break;
+        case RollbackToTargetVersion::kDisabled:
+          LOG(INFO) << "Policy disables rollbacks.";
+          result->rollback_allowed = false;
+          break;
+        case RollbackToTargetVersion::kRollbackWithFullPowerwash:
+          LOG(INFO) << "Policy allows rollbacks.";
+          result->rollback_allowed = true;
+          break;
+        case RollbackToTargetVersion::kMaxValue:
+          NOTREACHED();
+          // Don't add a default case to let the compiler warn about newly
+          // added enum values which should be added here.
       }
     }