update_engine: Implement ReleaseLtsTag

LTS (Long Term Support) requires reading the policy
ChromeOSReleaseLtsHint() to get the token that is used for defining the
LTS channel parameters. The value is sent in "ltstag" attribute of the
updatecheck.

BUG=chromium:1114784
TEST=sudo FEATURES=test emerge update_engine

Cq-Depend: chromium:2345311
Change-Id: I480e7920e9187ebe79cbde6c655252e432842608
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/2350580
Reviewed-by: Victor-Gabriel Savu <vsavu@google.com>
Reviewed-by: Jae Hoon Kim <kimjae@chromium.org>
Reviewed-by: Andrew Lassalle <andrewlassalle@chromium.org>
Tested-by: Victor-Gabriel Savu <vsavu@google.com>
Commit-Queue: Victor-Gabriel Savu <vsavu@google.com>
Commit-Queue: Amin Hassani <ahassani@chromium.org>
diff --git a/mock_update_attempter.h b/mock_update_attempter.h
index ad34802..cc05648 100644
--- a/mock_update_attempter.h
+++ b/mock_update_attempter.h
@@ -30,16 +30,17 @@
  public:
   using UpdateAttempter::UpdateAttempter;
 
-  MOCK_METHOD9(Update,
-               void(const std::string& app_version,
-                    const std::string& omaha_url,
-                    const std::string& target_channel,
-                    const std::string& target_version_prefix,
-                    bool rollback_allowed,
-                    bool rollback_data_save_requested,
-                    int rollback_allowed_milestones,
-                    bool obey_proxies,
-                    bool interactive));
+  MOCK_METHOD10(Update,
+                void(const std::string& app_version,
+                     const std::string& omaha_url,
+                     const std::string& target_channel,
+                     const std::string& lts_tag,
+                     const std::string& target_version_prefix,
+                     bool rollback_allowed,
+                     bool rollback_data_save_requested,
+                     int rollback_allowed_milestones,
+                     bool obey_proxies,
+                     bool interactive));
 
   MOCK_METHOD1(GetStatus, bool(update_engine::UpdateEngineStatus* out_status));
 
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index e608c07..adb95df 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -1528,6 +1528,7 @@
   request_params_.set_os_board("x86 generic<id");
   request_params_.set_current_channel("unittest_track&lt;");
   request_params_.set_target_channel("unittest_track&lt;");
+  request_params_.set_lts_tag("unittest_hint&lt;");
   request_params_.set_hwid("<OEM MODEL>");
   fake_prefs_.SetString(kPrefsOmahaCohort, "evil\nstring");
   fake_prefs_.SetString(kPrefsOmahaCohortHint, "evil&string\\");
@@ -1547,6 +1548,8 @@
   EXPECT_EQ(string::npos, post_str.find("x86 generic<id"));
   EXPECT_NE(string::npos, post_str.find("unittest_track&amp;lt;"));
   EXPECT_EQ(string::npos, post_str.find("unittest_track&lt;"));
+  EXPECT_NE(string::npos, post_str.find("unittest_hint&amp;lt;"));
+  EXPECT_EQ(string::npos, post_str.find("unittest_hint&lt;"));
   EXPECT_NE(string::npos, post_str.find("&lt;OEM MODEL&gt;"));
   EXPECT_EQ(string::npos, post_str.find("<OEM MODEL>"));
   EXPECT_NE(string::npos, post_str.find("cohort=\"evil\nstring\""));
@@ -1801,6 +1804,17 @@
   EXPECT_EQ(string::npos, post_str.find(omaha_cohort_hint));
 }
 
+TEST_F(OmahaRequestActionTest, TargetChannelHintTest) {
+  tuc_params_.http_response = fake_update_response_.GetNoUpdateResponse();
+  tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
+  tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
+  request_params_.set_lts_tag("hint>");
+
+  ASSERT_TRUE(TestUpdateCheck());
+
+  EXPECT_NE(string::npos, post_str.find("ltstag=\"hint&gt;\""));
+}
+
 void OmahaRequestActionTest::PingTest(bool ping_only) {
   NiceMock<MockPrefs> prefs;
   fake_system_state_.set_prefs(&prefs);
diff --git a/omaha_request_builder_xml.cc b/omaha_request_builder_xml.cc
index 2eb71bb..8add89f 100644
--- a/omaha_request_builder_xml.cc
+++ b/omaha_request_builder_xml.cc
@@ -154,6 +154,11 @@
             app_body += " rollback_allowed=\"true\"";
           }
         }
+        if (!params_->lts_tag().empty()) {
+          app_body += base::StringPrintf(
+              " ltstag=\"%s\"",
+              XmlEncodeWithDefault(params_->lts_tag()).c_str());
+        }
         app_body += "></updatecheck>\n";
       }
 
diff --git a/omaha_request_params.h b/omaha_request_params.h
index 76fc806..aad9290 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -148,6 +148,10 @@
     return target_version_prefix_;
   }
 
+  inline std::string lts_tag() const { return lts_tag_; }
+
+  inline void set_lts_tag(const std::string& hint) { lts_tag_ = hint; }
+
   inline void set_rollback_allowed(bool rollback_allowed) {
     rollback_allowed_ = rollback_allowed;
   }
@@ -367,6 +371,9 @@
   //   changed and cancel the current download attempt.
   std::string download_channel_;
 
+  // The value defining the parameters of the LTS (Long Term Support).
+  std::string lts_tag_;
+
   std::string hwid_;        // Hardware Qualification ID of the client
   std::string fw_version_;  // Chrome OS Firmware Version.
   std::string ec_version_;  // Chrome OS EC Version.
diff --git a/omaha_request_params_unittest.cc b/omaha_request_params_unittest.cc
index bfcbc32..110fb2b 100644
--- a/omaha_request_params_unittest.cc
+++ b/omaha_request_params_unittest.cc
@@ -236,6 +236,13 @@
   EXPECT_FALSE(params_.ToMoreStableChannel());
 }
 
+TEST_F(OmahaRequestParamsTest, TargetChannelHintTest) {
+  EXPECT_TRUE(params_.Init("", "", false));
+  const string kHint("foo-hint");
+  params_.set_lts_tag(kHint);
+  EXPECT_EQ(kHint, params_.lts_tag());
+}
+
 TEST_F(OmahaRequestParamsTest, ShouldPowerwashTest) {
   params_.mutable_image_props_.is_powerwash_allowed = false;
   EXPECT_FALSE(params_.ShouldPowerwash());
diff --git a/update_attempter.cc b/update_attempter.cc
index 60c2c36..0f0605d 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -247,6 +247,7 @@
 void UpdateAttempter::Update(const string& app_version,
                              const string& omaha_url,
                              const string& target_channel,
+                             const string& lts_tag,
                              const string& target_version_prefix,
                              bool rollback_allowed,
                              bool rollback_data_save_requested,
@@ -284,6 +285,7 @@
   if (!CalculateUpdateParams(app_version,
                              omaha_url,
                              target_channel,
+                             lts_tag,
                              target_version_prefix,
                              rollback_allowed,
                              rollback_data_save_requested,
@@ -359,6 +361,7 @@
 bool UpdateAttempter::CalculateUpdateParams(const string& app_version,
                                             const string& omaha_url,
                                             const string& target_channel,
+                                            const string& lts_tag,
                                             const string& target_version_prefix,
                                             bool rollback_allowed,
                                             bool rollback_data_save_requested,
@@ -378,6 +381,9 @@
   // Update the target version prefix.
   omaha_request_params_->set_target_version_prefix(target_version_prefix);
 
+  // Update the LTS support.
+  omaha_request_params_->set_lts_tag(lts_tag);
+
   // Set whether rollback is allowed.
   omaha_request_params_->set_rollback_allowed(rollback_allowed);
 
@@ -1103,6 +1109,7 @@
     Update(forced_app_version_,
            forced_omaha_url_,
            params.target_channel,
+           params.lts_tag,
            params.target_version_prefix,
            params.rollback_allowed,
            params.rollback_data_save_requested,
diff --git a/update_attempter.h b/update_attempter.h
index dd958f5..abd0bd4 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -85,6 +85,7 @@
   virtual void Update(const std::string& app_version,
                       const std::string& omaha_url,
                       const std::string& target_channel,
+                      const std::string& lts_tag,
                       const std::string& target_version_prefix,
                       bool rollback_allowed,
                       bool rollback_data_save_requested,
@@ -293,6 +294,7 @@
   FRIEND_TEST(UpdateAttempterTest, SessionIdTestOnOmahaRequestActions);
   FRIEND_TEST(UpdateAttempterTest, SetRollbackHappenedNotRollback);
   FRIEND_TEST(UpdateAttempterTest, SetRollbackHappenedRollback);
+  FRIEND_TEST(UpdateAttempterTest, TargetChannelHintSetAndReset);
   FRIEND_TEST(UpdateAttempterTest, TargetVersionPrefixSetAndReset);
   FRIEND_TEST(UpdateAttempterTest, UpdateAfterInstall);
   FRIEND_TEST(UpdateAttempterTest, UpdateAttemptFlagsCachedAtUpdateStart);
@@ -369,6 +371,7 @@
   bool CalculateUpdateParams(const std::string& app_version,
                              const std::string& omaha_url,
                              const std::string& target_channel,
+                             const std::string& lts_tag,
                              const std::string& target_version_prefix,
                              bool rollback_allowed,
                              bool rollback_data_save_requested,
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 0086dd5..7466aba 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -174,6 +174,7 @@
   void Update(const std::string& app_version,
               const std::string& omaha_url,
               const std::string& target_channel,
+              const std::string& lts_tag,
               const std::string& target_version_prefix,
               bool rollback_allowed,
               bool rollback_data_save_requested,
@@ -185,6 +186,7 @@
       UpdateAttempter::Update(app_version,
                               omaha_url,
                               target_channel,
+                              lts_tag,
                               target_version_prefix,
                               rollback_allowed,
                               rollback_data_save_requested,
@@ -425,7 +427,7 @@
 void UpdateAttempterTest::SessionIdTestChange() {
   EXPECT_NE(UpdateStatus::UPDATED_NEED_REBOOT, attempter_.status());
   const auto old_session_id = attempter_.session_id_;
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   EXPECT_NE(old_session_id, attempter_.session_id_);
   ScheduleQuitMainLoop();
 }
@@ -795,7 +797,7 @@
     EXPECT_CALL(*processor_, StartProcessing());
   }
 
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   loop_.PostTask(FROM_HERE,
                  base::Bind(&UpdateAttempterTest::UpdateTestVerify,
                             base::Unretained(this)));
@@ -995,7 +997,7 @@
   fake_system_state_.set_p2p_manager(&mock_p2p_manager);
   mock_p2p_manager.fake().SetP2PEnabled(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading_);
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1017,7 +1019,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(false);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1040,7 +1042,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1062,7 +1064,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   EXPECT_TRUE(actual_using_p2p_for_downloading());
   EXPECT_TRUE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -1089,6 +1091,7 @@
                     "",
                     "",
                     "",
+                    "",
                     false,
                     false,
                     /*rollback_allowed_milestones=*/0,
@@ -1123,7 +1126,7 @@
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
 
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   ScheduleQuitMainLoop();
@@ -1161,7 +1164,7 @@
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
 
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   // Make sure the file still exists.
@@ -1177,7 +1180,7 @@
   // However, if the count is already 0, it's not decremented. Test that.
   initial_value = 0;
   EXPECT_TRUE(fake_prefs.SetInt64(kPrefsUpdateCheckCount, initial_value));
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   EXPECT_TRUE(fake_prefs.Exists(kPrefsUpdateCheckCount));
   EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &new_value));
   EXPECT_EQ(initial_value, new_value);
@@ -1228,6 +1231,7 @@
                     "",
                     "",
                     "",
+                    "",
                     false,
                     false,
                     /*rollback_allowed_milestones=*/0,
@@ -1284,7 +1288,7 @@
   FakePrefs fake_prefs;
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   // Check that prefs have the correct values.
   int64_t update_count;
   EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &update_count));
@@ -1342,7 +1346,7 @@
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
   attempter_.Update(
-      "", "", "", "", false, false, 0, false, /* interactive = */ true);
+      "", "", "", "", "", false, false, 0, false, /* interactive = */ true);
   CheckStagingOff();
 
   ScheduleQuitMainLoop();
@@ -1363,7 +1367,7 @@
   SetUpStagingTest(kValidStagingSchedule, &fake_prefs);
 
   attempter_.Update(
-      "", "", "", "", false, false, 0, false, /* interactive = */ true);
+      "", "", "", "", "", false, false, 0, false, /* interactive = */ true);
   CheckStagingOff();
 
   ScheduleQuitMainLoop();
@@ -1692,20 +1696,31 @@
 
 TEST_F(UpdateAttempterTest, TargetVersionPrefixSetAndReset) {
   attempter_.CalculateUpdateParams(
-      "", "", "", "1234", false, false, 4, false, false);
+      "", "", "", "", "1234", false, false, 4, false, false);
   EXPECT_EQ("1234",
             fake_system_state_.request_params()->target_version_prefix());
 
   attempter_.CalculateUpdateParams(
-      "", "", "", "", false, 4, false, false, false);
+      "", "", "", "", "", false, 4, false, false, false);
   EXPECT_TRUE(
       fake_system_state_.request_params()->target_version_prefix().empty());
 }
 
+TEST_F(UpdateAttempterTest, TargetChannelHintSetAndReset) {
+  attempter_.CalculateUpdateParams(
+      "", "", "", "hint", "", false, false, 4, false, false);
+  EXPECT_EQ("hint", fake_system_state_.request_params()->lts_tag());
+
+  attempter_.CalculateUpdateParams(
+      "", "", "", "", "", false, 4, false, false, false);
+  EXPECT_TRUE(fake_system_state_.request_params()->lts_tag().empty());
+}
+
 TEST_F(UpdateAttempterTest, RollbackAllowedSetAndReset) {
   attempter_.CalculateUpdateParams("",
                                    "",
                                    "",
+                                   "",
                                    "1234",
                                    /*rollback_allowed=*/true,
                                    /*rollback_data_save_requested=*/false,
@@ -1719,6 +1734,7 @@
   attempter_.CalculateUpdateParams("",
                                    "",
                                    "",
+                                   "",
                                    "1234",
                                    /*rollback_allowed=*/false,
                                    /*rollback_data_save_requested=*/false,
@@ -1844,7 +1860,7 @@
               SetRollbackHappened(false))
       .Times(expected_reset ? 1 : 0);
   attempter_.policy_provider_ = std::move(mock_policy_provider);
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
   ScheduleQuitMainLoop();
 }
 
@@ -2185,7 +2201,7 @@
         .WillOnce(Return(false));
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
-  attempter_.Update("", "", "", "", false, false, 0, false, false);
+  attempter_.Update("", "", "", "", "", false, false, 0, false, false);
 
   EXPECT_EQ(token, attempter_.omaha_request_params_->autoupdate_token());
   ScheduleQuitMainLoop();
diff --git a/update_manager/android_things_policy.cc b/update_manager/android_things_policy.cc
index a76ea48..6362a73 100644
--- a/update_manager/android_things_policy.cc
+++ b/update_manager/android_things_policy.cc
@@ -58,6 +58,7 @@
   // Set the default return values.
   result->updates_enabled = true;
   result->target_channel.clear();
+  result->lts_tag.clear();
   result->target_version_prefix.clear();
   result->rollback_allowed = false;
   result->rollback_data_save_requested = false;
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
index c310e42..a4926f4 100644
--- a/update_manager/chromeos_policy.cc
+++ b/update_manager/chromeos_policy.cc
@@ -215,6 +215,7 @@
   // Set the default return values.
   result->updates_enabled = true;
   result->target_channel.clear();
+  result->lts_tag.clear();
   result->target_version_prefix.clear();
   result->rollback_allowed = false;
   result->rollback_allowed_milestones = -1;
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
index 414ac0d..f4ad165 100644
--- a/update_manager/chromeos_policy_unittest.cc
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -262,6 +262,8 @@
       new bool(false));
   fake_state_.device_policy_provider()->var_release_channel()->reset(
       new string("foo-channel"));
+  fake_state_.device_policy_provider()->var_release_lts_tag()->reset(
+      new string("foo-hint"));
 
   UpdateCheckParams result;
   ExpectPolicyStatus(
@@ -270,6 +272,7 @@
   EXPECT_EQ("1.2", result.target_version_prefix);
   EXPECT_EQ(5, result.rollback_allowed_milestones);
   EXPECT_EQ("foo-channel", result.target_channel);
+  EXPECT_EQ("foo-hint", result.lts_tag);
   EXPECT_FALSE(result.interactive);
 }
 
diff --git a/update_manager/default_policy.cc b/update_manager/default_policy.cc
index 81ab795..cc13c44 100644
--- a/update_manager/default_policy.cc
+++ b/update_manager/default_policy.cc
@@ -40,6 +40,7 @@
                                              UpdateCheckParams* result) const {
   result->updates_enabled = true;
   result->target_channel.clear();
+  result->lts_tag.clear();
   result->target_version_prefix.clear();
   result->rollback_allowed = false;
   result->rollback_allowed_milestones = -1;  // No version rolls should happen.
diff --git a/update_manager/device_policy_provider.h b/update_manager/device_policy_provider.h
index b68fe96..d63c416 100644
--- a/update_manager/device_policy_provider.h
+++ b/update_manager/device_policy_provider.h
@@ -44,6 +44,8 @@
 
   virtual Variable<bool>* var_release_channel_delegated() = 0;
 
+  virtual Variable<std::string>* var_release_lts_tag() = 0;
+
   virtual Variable<bool>* var_update_disabled() = 0;
 
   virtual Variable<std::string>* var_target_version_prefix() = 0;
diff --git a/update_manager/enterprise_device_policy_impl.cc b/update_manager/enterprise_device_policy_impl.cc
index dea38ba..fed50a9 100644
--- a/update_manager/enterprise_device_policy_impl.cc
+++ b/update_manager/enterprise_device_policy_impl.cc
@@ -126,6 +126,12 @@
       if (release_channel_p)
         result->target_channel = *release_channel_p;
     }
+
+    const string* release_lts_tag_p =
+        ec->GetValue(dp_provider->var_release_lts_tag());
+    if (release_lts_tag_p) {
+      result->lts_tag = *release_lts_tag_p;
+    }
   }
   return EvalStatus::kContinue;
 }
diff --git a/update_manager/fake_device_policy_provider.h b/update_manager/fake_device_policy_provider.h
index 86bdef1..352e51e 100644
--- a/update_manager/fake_device_policy_provider.h
+++ b/update_manager/fake_device_policy_provider.h
@@ -42,6 +42,10 @@
     return &var_release_channel_delegated_;
   }
 
+  FakeVariable<std::string>* var_release_lts_tag() override {
+    return &var_release_lts_tag_;
+  }
+
   FakeVariable<bool>* var_update_disabled() override {
     return &var_update_disabled_;
   }
@@ -98,6 +102,8 @@
                                                  kVariableModePoll};
   FakeVariable<bool> var_release_channel_delegated_{"release_channel_delegated",
                                                     kVariableModePoll};
+  FakeVariable<std::string> var_release_lts_tag_{"release_lts_tag",
+                                                 kVariableModePoll};
   FakeVariable<bool> var_update_disabled_{"update_disabled", kVariableModePoll};
   FakeVariable<std::string> var_target_version_prefix_{"target_version_prefix",
                                                        kVariableModePoll};
diff --git a/update_manager/policy.h b/update_manager/policy.h
index 844a4d0..9194c38 100644
--- a/update_manager/policy.h
+++ b/update_manager/policy.h
@@ -60,6 +60,8 @@
   int rollback_allowed_milestones;
   // A target channel, if so imposed by policy; otherwise, an empty string.
   std::string target_channel;
+  // Specifies if the channel hint, e.g. LTS (Long Term Support) updates.
+  std::string lts_tag;
 
   // Whether the allowed update is interactive (user-initiated) or periodic.
   bool interactive;
diff --git a/update_manager/real_device_policy_provider.cc b/update_manager/real_device_policy_provider.cc
index 55232f3..bd9d415 100644
--- a/update_manager/real_device_policy_provider.cc
+++ b/update_manager/real_device_policy_provider.cc
@@ -220,6 +220,7 @@
   UpdateVariable(&var_release_channel_, &DevicePolicy::GetReleaseChannel);
   UpdateVariable(&var_release_channel_delegated_,
                  &DevicePolicy::GetReleaseChannelDelegated);
+  UpdateVariable(&var_release_lts_tag_, &DevicePolicy::GetReleaseLtsTag);
   UpdateVariable(&var_update_disabled_, &DevicePolicy::GetUpdateDisabled);
   UpdateVariable(&var_target_version_prefix_,
                  &DevicePolicy::GetTargetVersionPrefix);
diff --git a/update_manager/real_device_policy_provider.h b/update_manager/real_device_policy_provider.h
index 9da052d..e6df18c 100644
--- a/update_manager/real_device_policy_provider.h
+++ b/update_manager/real_device_policy_provider.h
@@ -64,6 +64,10 @@
     return &var_release_channel_delegated_;
   }
 
+  Variable<std::string>* var_release_lts_tag() override {
+    return &var_release_lts_tag_;
+  }
+
   Variable<bool>* var_update_disabled() override {
     return &var_update_disabled_;
   }
@@ -191,6 +195,7 @@
   AsyncCopyVariable<std::string> var_release_channel_{"release_channel"};
   AsyncCopyVariable<bool> var_release_channel_delegated_{
       "release_channel_delegated"};
+  AsyncCopyVariable<std::string> var_release_lts_tag_{"release_lts_tag"};
   AsyncCopyVariable<bool> var_update_disabled_{"update_disabled"};
   AsyncCopyVariable<std::string> var_target_version_prefix_{
       "target_version_prefix"};
diff --git a/update_manager/real_device_policy_provider_unittest.cc b/update_manager/real_device_policy_provider_unittest.cc
index 84debd1..1384e6f 100644
--- a/update_manager/real_device_policy_provider_unittest.cc
+++ b/update_manager/real_device_policy_provider_unittest.cc
@@ -177,6 +177,7 @@
 
   UmTestUtils::ExpectVariableNotSet(provider_->var_release_channel());
   UmTestUtils::ExpectVariableNotSet(provider_->var_release_channel_delegated());
+  UmTestUtils::ExpectVariableNotSet(provider_->var_release_lts_tag());
   UmTestUtils::ExpectVariableNotSet(provider_->var_update_disabled());
   UmTestUtils::ExpectVariableNotSet(provider_->var_target_version_prefix());
   UmTestUtils::ExpectVariableNotSet(