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 <[email protected]>
Tested-by: Xiaochu Liu <[email protected]>
Reviewed-by: Amin Hassani <[email protected]>
Reviewed-by: Xiaochu Liu <[email protected]>
diff --git a/boot_control_chromeos.cc b/boot_control_chromeos.cc
index aa94d3c..f226a65 100644
--- a/boot_control_chromeos.cc
+++ b/boot_control_chromeos.cc
@@ -16,12 +16,15 @@
#include "update_engine/boot_control_chromeos.h"
+#include <memory>
#include <string>
+#include <utility>
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/strings/string_util.h>
+#include <brillo/update_engine/utils.h>
#include <rootdev/rootdev.h>
extern "C" {
@@ -141,6 +144,30 @@
bool BootControlChromeOS::GetPartitionDevice(const string& partition_name,
unsigned int slot,
string* device) const {
+ // Partition name prefixed with |kPartitionNamePrefixDlc| is a DLC.
+ if (base::StartsWith(partition_name,
+ brillo::chromeos_update_engine::kPartitionNamePrefixDlc,
+ base::CompareCase::SENSITIVE)) {
+ // Extract DLC ID from partition_name (DLC ID is the string after
+ // |kPartitionNamePrefixDlc| in partition_name).
+ const auto dlc_id = partition_name.substr(
+ strlen(brillo::chromeos_update_engine::kPartitionNamePrefixDlc));
+ if (dlc_id.empty()) {
+ LOG(ERROR) << " partition name does not contain dlc id:"
+ << partition_name;
+ return false;
+ }
+ *device =
+ base::FilePath(
+ brillo::chromeos_update_engine::kDlcInstallRootDirectoryEncrypted)
+ .Append(dlc_id)
+ .Append(slot == 0
+ ? brillo::chromeos_update_engine::kPartitionNameDlcA
+ : brillo::chromeos_update_engine::kPartitionNameDlcB)
+ .Append(brillo::chromeos_update_engine::kPartitionNameDlcImage)
+ .value();
+ return true;
+ }
int partition_num = GetPartitionNumber(partition_name, slot);
if (partition_num < 0)
return false;
diff --git a/client_library/client_dbus.cc b/client_library/client_dbus.cc
index 1072836..2257d2e 100644
--- a/client_library/client_dbus.cc
+++ b/client_library/client_dbus.cc
@@ -20,6 +20,7 @@
#include <dbus/bus.h>
#include <update_engine/dbus-constants.h>
+#include <update_engine/proto_bindings/update_engine.pb.h>
#include "update_engine/update_status_utils.h"
@@ -27,6 +28,7 @@
using dbus::Bus;
using org::chromium::UpdateEngineInterfaceProxy;
using std::string;
+using std::vector;
namespace update_engine {
namespace internal {
@@ -55,6 +57,24 @@
nullptr);
}
+bool DBusUpdateEngineClient::AttemptInstall(const string& omaha_url,
+ const vector<string>& dlc_ids) {
+ // Convert parameters into protobuf.
+ chromeos_update_engine::DlcParameters dlc_parameters;
+ dlc_parameters.set_omaha_url(omaha_url);
+ for (const auto& dlc_id : dlc_ids) {
+ chromeos_update_engine::DlcInfo* dlc_info = dlc_parameters.add_dlc_infos();
+ dlc_info->set_dlc_id(dlc_id);
+ }
+ string dlc_request;
+ if (dlc_parameters.SerializeToString(&dlc_request)) {
+ return proxy_->AttemptInstall(dlc_request, nullptr /* brillo::ErrorPtr* */);
+ } else {
+ LOG(ERROR) << "Fail to serialize a protobuf to a string.";
+ return false;
+ }
+}
+
bool DBusUpdateEngineClient::GetStatus(int64_t* out_last_checked_time,
double* out_progress,
UpdateStatus* out_update_status,
diff --git a/client_library/client_dbus.h b/client_library/client_dbus.h
index cec1665..0fa2cc3 100644
--- a/client_library/client_dbus.h
+++ b/client_library/client_dbus.h
@@ -41,6 +41,9 @@
const std::string& omaha_url,
bool at_user_request) override;
+ bool AttemptInstall(const std::string& omaha_url,
+ const std::vector<std::string>& dlc_ids) override;
+
bool GetStatus(int64_t* out_last_checked_time,
double* out_progress,
UpdateStatus* out_update_status,
diff --git a/client_library/include/update_engine/client.h b/client_library/include/update_engine/client.h
index be87c76..183768d 100644
--- a/client_library/include/update_engine/client.h
+++ b/client_library/include/update_engine/client.h
@@ -20,6 +20,7 @@
#include <cstdint>
#include <memory>
#include <string>
+#include <vector>
#include "update_engine/status_update_handler.h"
#include "update_engine/update_status.h"
@@ -47,6 +48,17 @@
const std::string& omaha_url,
bool at_user_request) = 0;
+ // Request the update_engine to install a list of DLC.
+ // |omaha_url|
+ // Force update_engine to look for updates from the given server. Passing
+ // empty indicates update_engine should use its default value. Note that
+ // update_engine will ignore this parameter in production mode to avoid
+ // pulling untrusted updates.
+ // |dlc_ids|
+ // A vector of DLC IDs.
+ virtual bool AttemptInstall(const std::string& omaha_url,
+ const std::vector<std::string>& dlc_ids) = 0;
+
// Returns the current status of the Update Engine.
//
// |out_last_checked_time|
diff --git a/common_service.cc b/common_service.cc
index 2fdd700..213267c 100644
--- a/common_service.cc
+++ b/common_service.cc
@@ -41,6 +41,7 @@
using brillo::ErrorPtr;
using brillo::string_utils::ToString;
using std::string;
+using std::vector;
using update_engine::UpdateAttemptFlags;
using update_engine::UpdateEngineStatus;
@@ -103,6 +104,17 @@
return true;
}
+bool UpdateEngineService::AttemptInstall(brillo::ErrorPtr* error,
+ const string& omaha_url,
+ const vector<string>& dlc_ids) {
+ if (!system_state_->update_attempter()->CheckForInstall(dlc_ids, omaha_url)) {
+ // TODO(xiaochu): support more detailed error messages.
+ LogAndSetError(error, FROM_HERE, "Could not schedule install operation.");
+ return false;
+ }
+ return true;
+}
+
bool UpdateEngineService::AttemptRollback(ErrorPtr* error, bool in_powerwash) {
LOG(INFO) << "Attempting rollback to non-active partitions.";
diff --git a/common_service.h b/common_service.h
index 824ef97..dbf897e 100644
--- a/common_service.h
+++ b/common_service.h
@@ -20,6 +20,7 @@
#include <inttypes.h>
#include <string>
+#include <vector>
#include <base/memory/ref_counted.h>
#include <brillo/errors/error.h>
@@ -52,6 +53,13 @@
int32_t in_flags_as_int,
bool* out_result);
+ // Attempts a DLC install operation.
+ // |omaha_url|: the URL to query for update.
+ // |dlc_ids|: a list of DLC IDs.
+ bool AttemptInstall(brillo::ErrorPtr* error,
+ const std::string& omaha_url,
+ const std::vector<std::string>& dlc_ids);
+
bool AttemptRollback(brillo::ErrorPtr* error, bool in_powerwash);
// Checks if the system rollback is available by verifying if the secondary
diff --git a/common_service_unittest.cc b/common_service_unittest.cc
index d9ef567..edf90b0 100644
--- a/common_service_unittest.cc
+++ b/common_service_unittest.cc
@@ -18,6 +18,7 @@
#include <gtest/gtest.h>
#include <string>
+#include <vector>
#include <brillo/errors/error.h>
#include <policy/libpolicy.h>
@@ -28,6 +29,7 @@
#include "update_engine/omaha_utils.h"
using std::string;
+using std::vector;
using testing::_;
using testing::Return;
using testing::SetArgPointee;
@@ -85,6 +87,21 @@
EXPECT_FALSE(result);
}
+TEST_F(UpdateEngineServiceTest, AttemptInstall) {
+ EXPECT_CALL(*mock_update_attempter_, CheckForInstall(_, _))
+ .WillOnce(Return(true));
+
+ EXPECT_TRUE(common_service_.AttemptInstall(&error_, "", {}));
+ EXPECT_EQ(nullptr, error_);
+}
+
+TEST_F(UpdateEngineServiceTest, AttemptInstallReturnsFalse) {
+ EXPECT_CALL(*mock_update_attempter_, CheckForInstall(_, _))
+ .WillOnce(Return(false));
+
+ EXPECT_FALSE(common_service_.AttemptInstall(&error_, "", {}));
+}
+
// SetChannel is allowed when there's no device policy (the device is not
// enterprise enrolled).
TEST_F(UpdateEngineServiceTest, SetChannelWithNoPolicy) {
diff --git a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
index a20f33f..f81d4ed 100644
--- a/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
+++ b/dbus_bindings/org.chromium.UpdateEngineInterface.dbus-xml
@@ -19,6 +19,9 @@
<!-- See AttemptUpdateFlags enum in update_engine/dbus-constants.h. -->
<arg type="i" name="flags" direction="in" />
</method>
+ <method name="AttemptInstall">
+ <arg type="s" name="dlc_request" direction="in" />
+ </method>
<method name="AttemptRollback">
<arg type="b" name="powerwash" direction="in" />
</method>
diff --git a/dbus_service.cc b/dbus_service.cc
index c7bc9f0..0eec01d 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -16,7 +16,12 @@
#include "update_engine/dbus_service.h"
-#include "update_engine/dbus-constants.h"
+#include <string>
+#include <vector>
+
+#include <update_engine/dbus-constants.h>
+#include <update_engine/proto_bindings/update_engine.pb.h>
+
#include "update_engine/dbus_connection.h"
#include "update_engine/update_status_utils.h"
@@ -25,6 +30,7 @@
using brillo::ErrorPtr;
using chromeos_update_engine::UpdateEngineService;
using std::string;
+using std::vector;
using update_engine::UpdateEngineStatus;
DBusUpdateEngineService::DBusUpdateEngineService(SystemState* system_state)
@@ -57,6 +63,28 @@
&result);
}
+bool DBusUpdateEngineService::AttemptInstall(ErrorPtr* error,
+ const string& dlc_request) {
+ // Parse the raw parameters into protobuf.
+ DlcParameters dlc_parameters;
+ if (!dlc_parameters.ParseFromString(dlc_request)) {
+ *error = brillo::Error::Create(
+ FROM_HERE, "update_engine", "INTERNAL", "parameters are invalid.");
+ return false;
+ }
+ // Extract fields from the protobuf.
+ vector<string> dlc_ids;
+ for (const auto& dlc_info : dlc_parameters.dlc_infos()) {
+ if (dlc_info.dlc_id().empty()) {
+ *error = brillo::Error::Create(
+ FROM_HERE, "update_engine", "INTERNAL", "parameters are invalid.");
+ return false;
+ }
+ dlc_ids.push_back(dlc_info.dlc_id());
+ }
+ return common_->AttemptInstall(error, dlc_parameters.omaha_url(), dlc_ids);
+}
+
bool DBusUpdateEngineService::AttemptRollback(ErrorPtr* error,
bool in_powerwash) {
return common_->AttemptRollback(error, in_powerwash);
diff --git a/dbus_service.h b/dbus_service.h
index e461fa6..134461b 100644
--- a/dbus_service.h
+++ b/dbus_service.h
@@ -49,6 +49,9 @@
const std::string& in_omaha_url,
int32_t in_flags_as_int) override;
+ bool AttemptInstall(brillo::ErrorPtr* error,
+ const std::string& dlc_request) override;
+
bool AttemptRollback(brillo::ErrorPtr* error, bool in_powerwash) override;
// Checks if the system rollback is available by verifying if the secondary
diff --git a/mock_update_attempter.h b/mock_update_attempter.h
index 405fdd6..eaa0785 100644
--- a/mock_update_attempter.h
+++ b/mock_update_attempter.h
@@ -18,6 +18,7 @@
#define UPDATE_ENGINE_MOCK_UPDATE_ATTEMPTER_H_
#include <string>
+#include <vector>
#include "update_engine/update_attempter.h"
@@ -51,6 +52,10 @@
const std::string& omaha_url,
UpdateAttemptFlags flags));
+ MOCK_METHOD2(CheckForInstall,
+ bool(const std::vector<std::string>& dlc_ids,
+ const std::string& omaha_url));
+
MOCK_METHOD0(RefreshDevicePolicy, void(void));
MOCK_CONST_METHOD0(consecutive_failed_update_checks, unsigned int(void));
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 0142172..2edf8c0 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -402,6 +402,7 @@
.id = params->GetAppId(),
.version = params->app_version(),
.product_components = params->product_components()};
+ // TODO(xiaochu): send update check flag only when operation is update.
string app_xml = GetAppXml(event,
params,
product_app,
@@ -424,6 +425,21 @@
install_date_in_days,
system_state);
}
+ // Create APP ID according to |dlc_id| (sticking the current AppID to the DLC
+ // ID with an underscode).
+ for (const auto& dlc_id : params->dlc_ids()) {
+ OmahaAppData dlc_app = {.id = params->GetAppId() + "_" + dlc_id,
+ .version = params->app_version()};
+ app_xml += GetAppXml(event,
+ params,
+ dlc_app,
+ ping_only,
+ include_ping,
+ ping_active_days,
+ ping_roll_call_days,
+ install_date_in_days,
+ system_state);
+ }
string install_source = base::StringPrintf("installsource=\"%s\" ",
(params->interactive() ? "ondemandupdate" : "scheduler"));
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 4d412ac..c512031 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -2506,7 +2506,7 @@
metrics::DownloadErrorCode::kUnset,
nullptr, // response
&post_data));
- // convert post_data to string
+ // Convert post_data to string.
string post_str(post_data.begin(), post_data.end());
EXPECT_NE(string::npos, post_str.find(
"appid=\"{11111111-1111-1111-1111-111111111111}\" "
@@ -3058,4 +3058,28 @@
EXPECT_FALSE(fake_prefs_.Exists(kPrefsUpdateFirstSeenAt));
}
+TEST_F(OmahaRequestActionTest, InstallTest) {
+ OmahaResponse response;
+ request_params_.set_dlc_ids({"dlc_no_0", "dlc_no_1"});
+ brillo::Blob post_data;
+ ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ true, // is_consumer_device
+ 0, // rollback_allowed_milestones
+ false, // is_policy_loaded
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ &post_data));
+ // Convert post_data to string.
+ string post_str(post_data.begin(), post_data.end());
+ for (const auto& dlc_id : request_params_.dlc_ids()) {
+ EXPECT_NE(string::npos,
+ post_str.find("appid=\"test-app-id_" + dlc_id + "\""));
+ }
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_request_params.cc b/omaha_request_params.cc
index 186ea61..4b72328 100644
--- a/omaha_request_params.cc
+++ b/omaha_request_params.cc
@@ -122,6 +122,10 @@
// Set the interactive flag accordingly.
interactive_ = in_interactive;
+
+ dlc_ids_.clear();
+ // Set false so it will do update by default.
+ is_install_ = false;
return true;
}
diff --git a/omaha_request_params.h b/omaha_request_params.h
index c8e26b5..1bebf1c 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <string>
+#include <vector>
#include <base/macros.h>
#include <base/time/time.h>
@@ -53,7 +54,8 @@
wall_clock_based_wait_enabled_(false),
update_check_count_wait_enabled_(false),
min_update_checks_needed_(kDefaultMinUpdateChecks),
- max_update_checks_allowed_(kDefaultMaxUpdateChecks) {}
+ max_update_checks_allowed_(kDefaultMaxUpdateChecks),
+ is_install_(false) {}
virtual ~OmahaRequestParams();
@@ -163,6 +165,12 @@
inline int64_t max_update_checks_allowed() const {
return max_update_checks_allowed_;
}
+ inline void set_dlc_ids(const std::vector<std::string>& dlc_ids) {
+ dlc_ids_ = dlc_ids;
+ }
+ inline std::vector<std::string> dlc_ids() const { return dlc_ids_; }
+ inline void set_is_install(bool is_install) { is_install_ = is_install; }
+ inline bool is_install() const { return is_install_; }
// Returns the app id corresponding to the current value of the
// download channel.
@@ -328,6 +336,14 @@
// When reading files, prepend root_ to the paths. Useful for testing.
std::string root_;
+ // A list of DLC ID to install.
+ std::vector<std::string> dlc_ids_;
+
+ // This variable defines whether the payload is being installed in the current
+ // partition. At the moment, his is used for installing DLCs on the current
+ // active partition instead of the inactive partition.
+ bool is_install_;
+
DISALLOW_COPY_AND_ASSIGN(OmahaRequestParams);
};
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index 7e6da5d..8549015 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -122,8 +122,13 @@
<< "Unable to save the update check response hash.";
}
- install_plan_.source_slot = system_state_->boot_control()->GetCurrentSlot();
- install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0;
+ if (params->is_install()) {
+ install_plan_.target_slot = system_state_->boot_control()->GetCurrentSlot();
+ install_plan_.source_slot = BootControlInterface::kInvalidSlot;
+ } else {
+ install_plan_.source_slot = system_state_->boot_control()->GetCurrentSlot();
+ install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0;
+ }
// The Omaha response doesn't include the channel name for this image, so we
// use the download_channel we used during the request to tag the target slot.
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index bdec86f..5281c88 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -140,7 +140,10 @@
SetString(kPrefsUpdateCheckResponseHash, expected_hash))
.WillOnce(Return(true));
- int slot = 1 - fake_system_state_.fake_boot_control()->GetCurrentSlot();
+ int slot =
+ fake_system_state_.request_params()->is_install()
+ ? fake_system_state_.fake_boot_control()->GetCurrentSlot()
+ : 1 - fake_system_state_.fake_boot_control()->GetCurrentSlot();
string key = kPrefsChannelOnSlotPrefix + std::to_string(slot);
EXPECT_CALL(*(fake_system_state_.mock_prefs()), SetString(key, testing::_))
.WillOnce(Return(true));
@@ -287,6 +290,25 @@
EXPECT_TRUE(install_plan.partitions.empty());
}
+TEST_F(OmahaResponseHandlerActionTest, InstallTest) {
+ OmahaResponse in;
+ in.update_exists = true;
+ in.version = "a.b.c.d";
+ in.packages.push_back(
+ {.payload_urls = {kLongName}, .size = 1, .hash = kPayloadHashHex});
+ in.packages.push_back(
+ {.payload_urls = {kLongName}, .size = 2, .hash = kPayloadHashHex});
+ in.more_info_url = "http://more/info";
+
+ OmahaRequestParams params(&fake_system_state_);
+ params.set_is_install(true);
+
+ fake_system_state_.set_request_params(¶ms);
+ InstallPlan install_plan;
+ EXPECT_TRUE(DoTest(in, "", &install_plan));
+ EXPECT_EQ(install_plan.source_slot, UINT_MAX);
+}
+
TEST_F(OmahaResponseHandlerActionTest, MultiPackageTest) {
OmahaResponse in;
in.update_exists = true;
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);
diff --git a/update_attempter.h b/update_attempter.h
index 8fcaba5..ebbd5ec 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -71,7 +71,8 @@
void Init();
// Initiates scheduling of update checks.
- virtual void ScheduleUpdates();
+ // Returns true if update check is scheduled.
+ virtual bool ScheduleUpdates();
// Checks for update and, if a newer version is available, attempts to update
// the system. Non-empty |in_app_version| or |in_update_url| prevents
@@ -147,6 +148,10 @@
const std::string& omaha_url,
UpdateAttemptFlags flags);
+ // This is the version of CheckForUpdate called by AttemptInstall API.
+ virtual bool CheckForInstall(const std::vector<std::string>& dlc_ids,
+ const std::string& omaha_url);
+
// This is the internal entry point for going through a rollback. This will
// attempt to run the postinstall on the non-active partition and set it as
// the partition to boot from. If |powerwash| is True, perform a powerwash
@@ -518,6 +523,12 @@
std::string forced_app_version_;
std::string forced_omaha_url_;
+ // A list of DLC IDs.
+ std::vector<std::string> dlc_ids_;
+ // Whether the operation is install (write to the current slot not the
+ // inactive slot).
+ bool is_install_;
+
// If this is not TimeDelta(), then that means staging is turned on.
base::TimeDelta staging_wait_time_;
chromeos_update_manager::StagingSchedule staging_schedule_;
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index b33f70d..0e1be2c 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -59,6 +59,7 @@
using policy::DevicePolicy;
using std::string;
using std::unique_ptr;
+using std::vector;
using testing::_;
using testing::DoAll;
using testing::Field;
@@ -90,13 +91,14 @@
// Wrap the update scheduling method, allowing us to opt out of scheduled
// updates for testing purposes.
- void ScheduleUpdates() override {
+ bool ScheduleUpdates() override {
schedule_updates_called_ = true;
if (do_schedule_updates_) {
UpdateAttempter::ScheduleUpdates();
} else {
LOG(INFO) << "[TEST] Update scheduling disabled.";
}
+ return true;
}
void EnableScheduleUpdates() { do_schedule_updates_ = true; }
void DisableScheduleUpdates() { do_schedule_updates_ = false; }
@@ -1173,6 +1175,19 @@
EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
}
+TEST_F(UpdateAttempterTest, CheckForInstallTest) {
+ fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+ fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
+ attempter_.CheckForInstall({}, "autest");
+ EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
+
+ attempter_.CheckForInstall({}, "autest-scheduled");
+ EXPECT_EQ(constants::kOmahaDefaultAUTestURL, attempter_.forced_omaha_url());
+
+ attempter_.CheckForInstall({}, "http://omaha.phishing");
+ EXPECT_EQ("", attempter_.forced_omaha_url());
+}
+
TEST_F(UpdateAttempterTest, TargetVersionPrefixSetAndReset) {
attempter_.CalculateUpdateParams("", "", "", "1234", false, false, false);
EXPECT_EQ("1234",
diff --git a/update_engine.gyp b/update_engine.gyp
index b18cc1c..b2c71c7 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -26,6 +26,11 @@
'deps': [
'libbrillo-<(libbase_ver)',
'libchrome-<(libbase_ver)',
+ # system_api depends on protobuf (or protobuf-lite). It must appear
+ # before protobuf here or the linker flags won't be in the right
+ # order.
+ 'system_api',
+ 'protobuf-lite',
],
# The -DUSE_* flags are passed from platform2.py. We use sane defaults
# here when these USE flags are not defined. You can set the default value
@@ -76,17 +81,6 @@
'variables': {
'proto_in_dir': '.',
'proto_out_dir': 'include/update_engine',
- 'exported_deps': [
- 'protobuf-lite',
- ],
- 'deps': ['<@(exported_deps)'],
- },
- 'all_dependent_settings': {
- 'variables': {
- 'deps': [
- '<@(exported_deps)',
- ],
- },
},
'sources': [
'update_metadata.proto',
diff --git a/update_engine_client.cc b/update_engine_client.cc
index b7096c5..9d1e093 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -26,6 +26,7 @@
#include <base/command_line.h>
#include <base/logging.h>
#include <base/macros.h>
+#include <base/strings/string_split.h>
#include <base/threading/platform_thread.h>
#include <brillo/daemons/daemon.h>
#include <brillo/flag_helper.h>
@@ -294,6 +295,8 @@
"Show the previous OS version used before the update reboot.");
DEFINE_bool(last_attempt_error, false, "Show the last attempt error.");
DEFINE_bool(eol_status, false, "Show the current end-of-life status.");
+ DEFINE_bool(install, false, "Requests an install.");
+ DEFINE_string(dlc_ids, "", "colon-separated list of DLC IDs.");
// Boilerplate init commands.
base::CommandLine::Init(argc_, argv_);
@@ -477,6 +480,28 @@
}
}
+ if (FLAGS_install) {
+ // Parse DLC IDs.
+ vector<string> dlc_ids;
+ if (!FLAGS_dlc_ids.empty()) {
+ dlc_ids = base::SplitString(
+ FLAGS_dlc_ids, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ }
+ if (dlc_ids.empty()) {
+ LOG(ERROR) << "dlc_ids is empty:" << FLAGS_dlc_ids;
+ return 1;
+ }
+ if (!client_->AttemptInstall(FLAGS_omaha_url, dlc_ids)) {
+ LOG(ERROR) << "AttemptInstall failed.";
+ return 1;
+ }
+ return 0;
+ } else if (!FLAGS_dlc_ids.empty()) {
+ LOG(ERROR) << "dlc_ids is not empty while install is not set:"
+ << FLAGS_dlc_ids;
+ return 1;
+ }
+
// Initiate an update check, if necessary.
if (do_update_request) {
LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";