update_engine: support downloading DLC

DLC (Downloadable Content) is a new concept that best can be described
as pieces of programs that are considered extensions of the rootfs. Each
DLC resides in the stateful partition and there are two slots for each
DLC (A and B, same as root and kernel partitions.) Each slot is treated
much like the root and kernel partitions themselves. This patch adds
initial support for installing and updating one or multiple DLCs. Each
DLC has a unique APP ID which is constructed such as
${PLATFORM_APPID}_${DLC_ID} where:

PLATFORM_APPID: Is the APP ID currently used for system updates (release
or canary)
DLC_ID: Is a unique ID given to a DLC for its lifetime.

The combination of these two unique IDs will generate a unique APP ID
that can be used for fetching update payloads for DLCs for each board.

Update engine traditionally is used for updating a system to a newer one
using the A/B slots. However, with DLCs residing in the stateful
partition, there is a chance that the stateful partition gets wiped or a
new DLC is required to be installed in the current slot. To solve this
problem, there is a new "Install" operation that allows downloading an
update payload for the current slot with the same version as we are now.
This CL adds AttemptInstall Dbus signal to be used for attempting an DLC
install operation. Furthermore, two new flags are added to the
update_engine_client utility:
--dlc_ids: A colon separated list of DLC IDs.
--install: Requests an install operation rather than an update.

BUG=chromium:879313
TEST=unittest, manual test
CQ-DEPEND=1266235

Change-Id: Ia9c9b702dc9d1bd47fbb10b30969baa0322993f6
Reviewed-on: https://chromium-review.googlesource.com/1195593
Commit-Ready: Xiaochu Liu <xiaochu@chromium.org>
Tested-by: Xiaochu Liu <xiaochu@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Xiaochu Liu <xiaochu@chromium.org>
diff --git a/update_attempter.cc b/update_attempter.cc
index 5cc92e6..581b901 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -159,9 +159,9 @@
     status_ = UpdateStatus::IDLE;
 }
 
-void UpdateAttempter::ScheduleUpdates() {
+bool UpdateAttempter::ScheduleUpdates() {
   if (IsUpdateRunningOrScheduled())
-    return;
+    return false;
 
   chromeos_update_manager::UpdateManager* const update_manager =
       system_state_->update_manager();
@@ -172,6 +172,7 @@
   // starvation due to a transient bug.
   update_manager->AsyncPolicyRequest(callback, &Policy::UpdateCheckAllowed);
   waiting_for_scheduled_check_ = true;
+  return true;
 }
 
 void UpdateAttempter::CertificateChecked(ServerToCheck server_to_check,
@@ -418,6 +419,9 @@
     // target channel.
     omaha_request_params_->UpdateDownloadChannel();
   }
+  // Set the DLC ID list.
+  omaha_request_params_->set_dlc_ids(dlc_ids_);
+  omaha_request_params_->set_is_install(is_install_);
 
   LOG(INFO) << "target_version_prefix = "
             << omaha_request_params_->target_version_prefix()
@@ -793,6 +797,8 @@
 bool UpdateAttempter::CheckForUpdate(const string& app_version,
                                      const string& omaha_url,
                                      UpdateAttemptFlags flags) {
+  dlc_ids_.clear();
+  is_install_ = false;
   bool interactive = !(flags & UpdateAttemptFlags::kFlagNonInteractive);
 
   if (interactive && status_ != UpdateStatus::IDLE) {
@@ -843,6 +849,37 @@
   return true;
 }
 
+bool UpdateAttempter::CheckForInstall(const vector<string>& dlc_ids,
+                                      const string& omaha_url) {
+  dlc_ids_ = dlc_ids;
+  is_install_ = true;
+  forced_omaha_url_.clear();
+
+  // Certain conditions must be met to allow setting custom version and update
+  // server URLs. However, kScheduledAUTestURLRequest and kAUTestURLRequest are
+  // always allowed regardless of device state.
+  if (IsAnyUpdateSourceAllowed()) {
+    forced_omaha_url_ = omaha_url;
+  }
+  if (omaha_url == kScheduledAUTestURLRequest) {
+    forced_omaha_url_ = constants::kOmahaDefaultAUTestURL;
+  } else if (omaha_url == kAUTestURLRequest) {
+    forced_omaha_url_ = constants::kOmahaDefaultAUTestURL;
+  }
+
+  if (!ScheduleUpdates()) {
+    if (forced_update_pending_callback_.get()) {
+      // Make sure that a scheduling request is made prior to calling the forced
+      // update pending callback.
+      ScheduleUpdates();
+      forced_update_pending_callback_->Run(true, true);
+      return true;
+    }
+    return false;
+  }
+  return true;
+}
+
 bool UpdateAttempter::RebootIfNeeded() {
 #ifdef __ANDROID__
   if (status_ != UpdateStatus::UPDATED_NEED_REBOOT) {
@@ -982,7 +1019,10 @@
   attempt_error_code_ = utils::GetBaseErrorCode(code);
 
   if (code == ErrorCode::kSuccess) {
-    WriteUpdateCompletedMarker();
+    // For install operation, we do not mark update complete since we do not
+    // need reboot.
+    if (!is_install_)
+      WriteUpdateCompletedMarker();
     ReportTimeToUpdateAppliedMetric();
 
     prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);