Merge aosp/upstream-master into aosp/master.
Added BinderUpdateEngineClient::AttemptInstall().
Test: update_engine_unittests
Change-Id: Id6911f49d763b0d572658acb7d66857016bf6969
diff --git a/OWNERS b/OWNERS
index 0bf7587..07ee38e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -7,3 +7,4 @@
# Chromium OS maintainers:
benchan@google.com
ahassani@google.com
+xiaochu@google.com
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index 192e6ab..9490096 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -76,4 +76,12 @@
send_interface="org.chromium.UpdateEngineInterface"
send_member="GetStatus"/>
</policy>
+ <policy user="dlcservice">
+ <allow send_destination="org.chromium.UpdateEngine"
+ send_interface="org.chromium.UpdateEngineInterface"
+ send_member="GetStatus"/>
+ <allow send_destination="org.chromium.UpdateEngine"
+ send_interface="org.chromium.UpdateEngineInterface"
+ send_member="AttemptInstall"/>
+ </policy>
</busconfig>
diff --git a/boot_control_chromeos.cc b/boot_control_chromeos.cc
index 4fa1268..3dee660 100644
--- a/boot_control_chromeos.cc
+++ b/boot_control_chromeos.cc
@@ -43,6 +43,12 @@
const char* kAndroidPartitionNameKernel = "boot";
const char* kAndroidPartitionNameRoot = "system";
+const char kDlcInstallRootDirectoryEncrypted[] = "/home/chronos/dlc";
+const char kPartitionNamePrefixDlc[] = "dlc_";
+const char kPartitionNameDlcA[] = "dlc_a";
+const char kPartitionNameDlcB[] = "dlc_b";
+const char kPartitionNameDlcImage[] = "dlc.img";
+
// Returns the currently booted rootfs partition. "/dev/sda3", for example.
string GetBootDevice() {
char boot_path[PATH_MAX];
@@ -143,6 +149,26 @@
bool BootControlChromeOS::GetPartitionDevice(const string& partition_name,
unsigned int slot,
string* device) const {
+ // Partition name prefixed with |kPartitionNamePrefixDlc| is a DLC module.
+ if (base::StartsWith(partition_name,
+ kPartitionNamePrefixDlc,
+ base::CompareCase::SENSITIVE)) {
+ // Extract DLC module ID from partition_name (DLC module ID is the string
+ // after |kPartitionNamePrefixDlc| in partition_name).
+ const auto dlc_module_id =
+ partition_name.substr(strlen(kPartitionNamePrefixDlc));
+ if (dlc_module_id.empty()) {
+ LOG(ERROR) << " partition name does not contain DLC module ID:"
+ << partition_name;
+ return false;
+ }
+ *device = base::FilePath(kDlcInstallRootDirectoryEncrypted)
+ .Append(dlc_module_id)
+ .Append(slot == 0 ? kPartitionNameDlcA : kPartitionNameDlcB)
+ .Append(kPartitionNameDlcImage)
+ .value();
+ return true;
+ }
int partition_num = GetPartitionNumber(partition_name, slot);
if (partition_num < 0)
return false;
diff --git a/client_library/client_binder.cc b/client_library/client_binder.cc
index 54b33ed..5c22f84 100644
--- a/client_library/client_binder.cc
+++ b/client_library/client_binder.cc
@@ -58,6 +58,11 @@
.isOk();
}
+bool BinderUpdateEngineClient::AttemptInstall(
+ const string& omaha_url, const std::vector<string>& dlc_module_ids) {
+ return false;
+}
+
bool BinderUpdateEngineClient::GetStatus(int64_t* out_last_checked_time,
double* out_progress,
UpdateStatus* out_update_status,
diff --git a/client_library/client_binder.h b/client_library/client_binder.h
index 17f2beb..b3c8940 100644
--- a/client_library/client_binder.h
+++ b/client_library/client_binder.h
@@ -47,6 +47,9 @@
const std::string& omaha_url,
bool at_user_request) override;
+ bool AttemptInstall(const std::string& omaha_url,
+ const std::vector<std::string>& dlc_module_ids) override;
+
bool GetStatus(int64_t* out_last_checked_time,
double* out_progress,
UpdateStatus* out_update_status,
diff --git a/client_library/client_dbus.cc b/client_library/client_dbus.cc
index 1072836..e0d1f64 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_module_ids) {
+ // Convert parameters into protobuf.
+ chromeos_update_engine::DlcParameters dlc_parameters;
+ dlc_parameters.set_omaha_url(omaha_url);
+ for (const auto& dlc_module_id : dlc_module_ids) {
+ chromeos_update_engine::DlcInfo* dlc_info = dlc_parameters.add_dlc_infos();
+ dlc_info->set_dlc_id(dlc_module_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..a186d45 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_module_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..1bc6111 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,18 @@
const std::string& omaha_url,
bool at_user_request) = 0;
+ // Request the update_engine to install a list of DLC modules.
+ // |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_module_ids|
+ // A list of DLC module IDs.
+ virtual bool AttemptInstall(
+ const std::string& omaha_url,
+ const std::vector<std::string>& dlc_module_ids) = 0;
+
// Returns the current status of the Update Engine.
//
// |out_last_checked_time|
diff --git a/common/dlcservice.h b/common/dlcservice.h
new file mode 100644
index 0000000..9dae560
--- /dev/null
+++ b/common/dlcservice.h
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_DLCSERVICE_H_
+#define UPDATE_ENGINE_COMMON_DLCSERVICE_H_
+
+#include <memory>
+
+#include "update_engine/common/dlcservice_interface.h"
+
+namespace chromeos_update_engine {
+
+// This factory function creates a new DlcServiceInterface instance for the
+// current platform.
+std::unique_ptr<DlcServiceInterface> CreateDlcService();
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_COMMON_DLCSERVICE_H_
diff --git a/common/dlcservice_interface.h b/common/dlcservice_interface.h
new file mode 100644
index 0000000..aa24105
--- /dev/null
+++ b/common/dlcservice_interface.h
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_DLCSERVICE_INTERFACE_H_
+#define UPDATE_ENGINE_COMMON_DLCSERVICE_INTERFACE_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+namespace chromeos_update_engine {
+
+// The abstract dlcservice interface defines the interaction with the
+// platform's dlcservice.
+class DlcServiceInterface {
+ public:
+ virtual ~DlcServiceInterface() = default;
+
+ // Returns true and a list of installed DLC module ids in |dlc_module_ids|.
+ // On failure it returns false.
+ virtual bool GetInstalled(std::vector<std::string>* dlc_module_ids) = 0;
+
+ protected:
+ DlcServiceInterface() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DlcServiceInterface);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_COMMON_DLCSERVICE_INTERFACE_H_
diff --git a/common/dlcservice_stub.cc b/common/dlcservice_stub.cc
new file mode 100644
index 0000000..c5f9306
--- /dev/null
+++ b/common/dlcservice_stub.cc
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/common/dlcservice_stub.h"
+
+#include <memory>
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+std::unique_ptr<DlcServiceInterface> CreateDlcService() {
+ return std::make_unique<DlcServiceStub>();
+}
+
+bool DlcServiceStub::GetInstalled(std::vector<std::string>* dlc_module_ids) {
+ if (dlc_module_ids)
+ dlc_module_ids->clear();
+ return true;
+}
+
+} // namespace chromeos_update_engine
diff --git a/common/dlcservice_stub.h b/common/dlcservice_stub.h
new file mode 100644
index 0000000..4e12c11
--- /dev/null
+++ b/common/dlcservice_stub.h
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_COMMON_DLCSERVICE_STUB_H_
+#define UPDATE_ENGINE_COMMON_DLCSERVICE_STUB_H_
+
+#include <string>
+#include <vector>
+
+#include "update_engine/common/dlcservice_interface.h"
+
+namespace chromeos_update_engine {
+
+// An implementation of the DlcServiceInterface that does nothing.
+class DlcServiceStub : public DlcServiceInterface {
+ public:
+ DlcServiceStub() = default;
+ ~DlcServiceStub() = default;
+
+ // BootControlInterface overrides.
+ bool GetInstalled(std::vector<std::string>* dlc_module_ids) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DlcServiceStub);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_COMMON_DLCSERVICE_STUB_H_
diff --git a/common/platform_constants_chromeos.cc b/common/platform_constants_chromeos.cc
index 3ebcf8a..3653432 100644
--- a/common/platform_constants_chromeos.cc
+++ b/common/platform_constants_chromeos.cc
@@ -22,7 +22,7 @@
const char kOmahaDefaultProductionURL[] =
"https://tools.google.com/service/update2";
const char kOmahaDefaultAUTestURL[] =
- "https://omaha.sandbox.google.com/service/update2";
+ "https://omaha-qa.sandbox.google.com/service/update2";
const char kOmahaUpdaterID[] = "ChromeOSUpdateEngine";
const char kOmahaPlatformName[] = "Chrome OS";
const char kUpdatePayloadPublicKeyPath[] =
diff --git a/common/utils.h b/common/utils.h
index f7f285b..e55a6e5 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -386,7 +386,9 @@
: processor_(processor),
action_(action),
code_(ErrorCode::kError),
- should_complete_(true) {}
+ should_complete_(true) {
+ CHECK(processor_);
+ }
~ScopedActionCompleter() {
if (should_complete_)
processor_->ActionComplete(action_, code_);
diff --git a/common_service.cc b/common_service.cc
index c88d940..0f88151 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,18 @@
return true;
}
+bool UpdateEngineService::AttemptInstall(brillo::ErrorPtr* error,
+ const string& omaha_url,
+ const vector<string>& dlc_module_ids) {
+ if (!system_state_->update_attempter()->CheckForInstall(dlc_module_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..f93855d 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 module install operation.
+ // |omaha_url|: the URL to query for update.
+ // |dlc_module_ids|: a list of DLC module IDs.
+ bool AttemptInstall(brillo::ErrorPtr* error,
+ const std::string& omaha_url,
+ const std::vector<std::string>& dlc_module_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/connection_manager.cc b/connection_manager.cc
index 4063f24..a048f5f 100644
--- a/connection_manager.cc
+++ b/connection_manager.cc
@@ -138,8 +138,11 @@
if (!default_service_path.IsValid())
return false;
// Shill uses the "/" service path to indicate that it is not connected.
- if (default_service_path.value() == "/")
- return false;
+ if (default_service_path.value() == "/") {
+ *out_type = ConnectionType::kDisconnected;
+ *out_tethering = ConnectionTethering::kUnknown;
+ return true;
+ }
TEST_AND_RETURN_FALSE(
GetServicePathProperties(default_service_path, out_type, out_tethering));
return true;
diff --git a/connection_manager.h b/connection_manager.h
index dc563ef..d8527a3 100644
--- a/connection_manager.h
+++ b/connection_manager.h
@@ -47,8 +47,8 @@
bool IsAllowedConnectionTypesForUpdateSet() const override;
private:
- // Returns (via out_path) the default network path, or empty string if
- // there's no network up. Returns true on success.
+ // Returns (via out_path) the default network path, or "/" if there's no
+ // network up. Returns true on success.
bool GetDefaultServicePath(dbus::ObjectPath* out_path);
bool GetServicePathProperties(const dbus::ObjectPath& path,
diff --git a/connection_manager_unittest.cc b/connection_manager_unittest.cc
index 85b8c57..7cd858d 100644
--- a/connection_manager_unittest.cc
+++ b/connection_manager_unittest.cc
@@ -75,6 +75,9 @@
const char* service_type,
const char* physical_technology,
ConnectionType expected_type);
+
+ void TestWithServiceDisconnected(ConnectionType expected_type);
+
void TestWithServiceTethering(
const char* service_tethering,
ConnectionTethering expected_tethering);
@@ -170,6 +173,18 @@
fake_shill_proxy_->GetManagerProxy());
}
+void ConnectionManagerTest::TestWithServiceDisconnected(
+ ConnectionType expected_type) {
+ SetManagerReply("/", true);
+
+ ConnectionType type;
+ ConnectionTethering tethering;
+ EXPECT_TRUE(cmut_.GetConnectionProperties(&type, &tethering));
+ EXPECT_EQ(expected_type, type);
+ testing::Mock::VerifyAndClearExpectations(
+ fake_shill_proxy_->GetManagerProxy());
+}
+
TEST_F(ConnectionManagerTest, SimpleTest) {
TestWithServiceType(shill::kTypeEthernet, nullptr, ConnectionType::kEthernet);
TestWithServiceType(shill::kTypeWifi, nullptr, ConnectionType::kWifi);
@@ -203,6 +218,10 @@
TestWithServiceType("foo", nullptr, ConnectionType::kUnknown);
}
+TEST_F(ConnectionManagerTest, DisconnectTest) {
+ TestWithServiceDisconnected(ConnectionType::kDisconnected);
+}
+
TEST_F(ConnectionManagerTest, AllowUpdatesOverEthernetTest) {
// Updates over Ethernet are allowed even if there's no policy.
EXPECT_TRUE(cmut_.IsUpdateAllowedOver(ConnectionType::kEthernet,
diff --git a/connection_utils.cc b/connection_utils.cc
index 9b6b526..aeb0163 100644
--- a/connection_utils.cc
+++ b/connection_utils.cc
@@ -18,6 +18,12 @@
#include <shill/dbus-constants.h>
+namespace {
+// Not defined by shill since we don't use this outside of UE.
+constexpr char kTypeDisconnected[] = "Disconnected";
+constexpr char kTypeUnknown[] = "Unknown";
+} // namespace
+
namespace chromeos_update_engine {
namespace connection_utils {
@@ -32,6 +38,8 @@
return ConnectionType::kBluetooth;
} else if (type_str == shill::kTypeCellular) {
return ConnectionType::kCellular;
+ } else if (type_str == kTypeDisconnected) {
+ return ConnectionType::kDisconnected;
}
return ConnectionType::kUnknown;
}
@@ -59,10 +67,12 @@
return shill::kTypeBluetooth;
case ConnectionType::kCellular:
return shill::kTypeCellular;
+ case ConnectionType::kDisconnected:
+ return kTypeDisconnected;
case ConnectionType::kUnknown:
- return "Unknown";
+ return kTypeUnknown;
}
- return "Unknown";
+ return kTypeUnknown;
}
} // namespace connection_utils
diff --git a/connection_utils.h b/connection_utils.h
index e385517..d5133a1 100644
--- a/connection_utils.h
+++ b/connection_utils.h
@@ -22,6 +22,7 @@
namespace chromeos_update_engine {
enum class ConnectionType {
+ kDisconnected,
kEthernet,
kWifi,
kWimax,
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..03425b6 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,29 @@
&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_module_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_module_ids.push_back(dlc_info.dlc_id());
+ }
+ return common_->AttemptInstall(
+ error, dlc_parameters.omaha_url(), dlc_module_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/dlcservice_chromeos.cc b/dlcservice_chromeos.cc
new file mode 100644
index 0000000..e95f08f
--- /dev/null
+++ b/dlcservice_chromeos.cc
@@ -0,0 +1,54 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/dlcservice_chromeos.h"
+
+#include <dlcservice/dbus-proxies.h>
+#include <dlcservice/proto_bindings/dlcservice.pb.h>
+
+#include "update_engine/dbus_connection.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+std::unique_ptr<DlcServiceInterface> CreateDlcService() {
+ return std::make_unique<DlcServiceChromeOS>();
+}
+
+bool DlcServiceChromeOS::GetInstalled(vector<string>* dlc_module_ids) {
+ if (!dlc_module_ids)
+ return false;
+ org::chromium::DlcServiceInterfaceProxy dlcservice_proxy(
+ DBusConnection::Get()->GetDBus());
+ string dlc_module_list_str;
+ if (!dlcservice_proxy.GetInstalled(&dlc_module_list_str, nullptr)) {
+ LOG(ERROR) << "dlcservice does not return installed DLC module list.";
+ return false;
+ }
+ dlcservice::DlcModuleList dlc_module_list;
+ if (!dlc_module_list.ParseFromString(dlc_module_list_str)) {
+ LOG(ERROR) << "Errors parsing DlcModuleList protobuf.";
+ return false;
+ }
+ for (const auto& dlc_module_info : dlc_module_list.dlc_module_infos()) {
+ dlc_module_ids->emplace_back(dlc_module_info.dlc_id());
+ }
+ return true;
+}
+
+} // namespace chromeos_update_engine
diff --git a/dlcservice_chromeos.h b/dlcservice_chromeos.h
new file mode 100644
index 0000000..8d103c1
--- /dev/null
+++ b/dlcservice_chromeos.h
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
+#define UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "update_engine/common/dlcservice_interface.h"
+
+namespace chromeos_update_engine {
+
+// The Chrome OS implementation of the DlcServiceInterface. This interface
+// interacts with dlcservice via D-Bus.
+class DlcServiceChromeOS : public DlcServiceInterface {
+ public:
+ DlcServiceChromeOS() = default;
+ ~DlcServiceChromeOS() = default;
+
+ // BootControlInterface overrides.
+ bool GetInstalled(std::vector<std::string>* dlc_module_ids) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DlcServiceChromeOS);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_DLCSERVICE_CHROMEOS_H_
diff --git a/fake_system_state.h b/fake_system_state.h
index 67ad3aa..627bd5c 100644
--- a/fake_system_state.h
+++ b/fake_system_state.h
@@ -100,6 +100,8 @@
return power_manager_;
}
+ inline DlcServiceInterface* dlcservice() override { return dlcservice_; }
+
inline bool system_rebooted() override { return fake_system_rebooted_; }
// Setters for the various members, can be used for overriding the default
@@ -165,6 +167,10 @@
fake_system_rebooted_ = system_rebooted;
}
+ inline void set_dlcservice(DlcServiceInterface* dlcservice) {
+ dlcservice_ = dlcservice;
+ }
+
// Getters for the built-in default implementations. These return the actual
// concrete type of each implementation. For additional safety, they will fail
// whenever the requested default was overridden by a different
@@ -261,6 +267,7 @@
P2PManager* p2p_manager_;
chromeos_update_manager::UpdateManager* update_manager_;
PowerManagerInterface* power_manager_{&mock_power_manager_};
+ DlcServiceInterface* dlcservice_;
// Other object pointers (not preinitialized).
const policy::DevicePolicy* device_policy_;
diff --git a/fuzz/xml.dict b/fuzz/xml.dict
new file mode 100644
index 0000000..8449cb0
--- /dev/null
+++ b/fuzz/xml.dict
@@ -0,0 +1,125 @@
+#
+# AFL dictionary for XML
+# ----------------------
+#
+# Several basic syntax elements and attributes, modeled on libxml2.
+#
+# Created by Michal Zalewski <lcamtuf@google.com>
+#
+
+attr_encoding=" encoding=\"1\""
+attr_generic=" a=\"1\""
+attr_href=" href=\"1\""
+attr_standalone=" standalone=\"no\""
+attr_version=" version=\"1\""
+attr_xml_base=" xml:base=\"1\""
+attr_xml_id=" xml:id=\"1\""
+attr_xml_lang=" xml:lang=\"1\""
+attr_xml_space=" xml:space=\"1\""
+attr_xmlns=" xmlns=\"1\""
+
+entity_builtin="<"
+entity_decimal=""
+entity_external="&a;"
+entity_hex=""
+
+# keywords
+"ANY"
+"ATTLIST"
+"CDATA"
+"DOCTYPE"
+"ELEMENT"
+"EMPTY"
+"ENTITIES"
+"ENTITY"
+"FIXED"
+"ID"
+"IDREF"
+"IDREFS"
+"IGNORE"
+"IMPLIED"
+"INCLUDE"
+"NDATA"
+"NMTOKEN"
+"NMTOKENS"
+"NOTATION"
+"PCDATA"
+"PUBLIC"
+"REQUIRED"
+"SYSTEM"
+
+# Various tag parts
+"<"
+">"
+"/>"
+"</"
+"<?"
+"?>"
+"<!"
+"!>"
+"[]"
+"]]"
+"<![CDATA["
+"<![CDATA[]]>"
+"\"\""
+"''"
+"=\"\""
+"=''"
+
+# DTD
+"<!ATTLIST"
+"<!DOCTYPE"
+"<!ELEMENT"
+"<!ENTITY"
+"<![IGNORE["
+"<![INCLUDE["
+"<!NOTATION"
+"#CDATA"
+"#FIXED"
+"#IMPLIED"
+"#PCDATA"
+"#REQUIRED"
+
+# Encodings
+"ISO-8859-1"
+"US-ASCII"
+"UTF-8"
+"UTF-16"
+"UTF-16BE"
+"UTF-16LE"
+
+# Namespaces and schemas
+"xmlns"
+"xmlns:"
+"xmlns:xhtml=\"http://www.w3.org/1999/xhtml\""
+"xmlns:xml=\"http://www.w3.org/XML/1998/namespace\""
+"xmlns:xmlns=\"http://www.w3.org/2000/xmlns\""
+
+string_col_fallback=":fallback"
+string_col_generic=":a"
+string_col_include=":include"
+string_dashes="--"
+string_parentheses="()"
+string_percent="%a"
+string_schema=":schema"
+string_ucs4="UCS-4"
+tag_close="</a>"
+tag_open="<a>"
+tag_open_close="<a />"
+
+
+"<?xml?>"
+"http://docboo"
+"http://www.w"
+"he30"
+"he2"
+"IET"
+"FDF-10"
+"aDUCS-4OPveb:"
+"a>"
+"UT"
+"xMl"
+"/usr/share/sg"
+"ha07"
+"http://www.oa"
+"cle"
diff --git a/metrics_constants.h b/metrics_constants.h
index abec2ad..eabb8fb 100644
--- a/metrics_constants.h
+++ b/metrics_constants.h
@@ -107,14 +107,15 @@
//
// This is used in the UpdateEngine.Attempt.ConnectionType histogram.
enum class ConnectionType {
- kUnknown, // Unknown.
- kEthernet, // Ethernet.
- kWifi, // Wireless.
- kWimax, // WiMax.
- kBluetooth, // Bluetooth.
- kCellular, // Cellular.
- kTetheredEthernet, // Tethered (Ethernet).
- kTetheredWifi, // Tethered (Wifi).
+ kUnknown = 0, // Unknown.
+ kEthernet = 1, // Ethernet.
+ kWifi = 2, // Wireless.
+ kWimax = 3, // WiMax.
+ kBluetooth = 4, // Bluetooth.
+ kCellular = 5, // Cellular.
+ kTetheredEthernet = 6, // Tethered (Ethernet).
+ kTetheredWifi = 7, // Tethered (Wifi).
+ kDisconnected = 8, // Disconnected.
kNumConstants,
kUnset = -1
diff --git a/metrics_reporter_android.h b/metrics_reporter_android.h
index 8a27ef6..e320c12 100644
--- a/metrics_reporter_android.h
+++ b/metrics_reporter_android.h
@@ -91,6 +91,9 @@
int kernel_max_rollforward_version,
bool kernel_max_rollforward_success) override {}
+ void ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) override {}
+
private:
DISALLOW_COPY_AND_ASSIGN(MetricsReporterAndroid);
};
diff --git a/metrics_reporter_interface.h b/metrics_reporter_interface.h
index b677aaa..fce8bfd 100644
--- a/metrics_reporter_interface.h
+++ b/metrics_reporter_interface.h
@@ -226,6 +226,22 @@
virtual void ReportKeyVersionMetrics(int kernel_min_version,
int kernel_max_rollforward_version,
bool kernel_max_rollforward_success) = 0;
+
+ // Helper function to report the duration between an update being seen by the
+ // client to the update being applied. Updates are not always immediately
+ // applied when seen, several enterprise policies can affect when an update
+ // would actually be downloaded and applied.
+ //
+ // This metric should only be reported for enterprise enrolled devices.
+ //
+ // The following metrics are reported from this function:
+ // If |has_time_restriction_policy| is false:
+ // |kMetricSuccessfulUpdateDurationFromSeenDays|
+ // If |has_time_restriction_policy| is true:
+ // |kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays|
+ //
+ virtual void ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) = 0;
};
} // namespace chromeos_update_engine
diff --git a/metrics_reporter_omaha.cc b/metrics_reporter_omaha.cc
index f0c6643..3ae4f4b 100644
--- a/metrics_reporter_omaha.cc
+++ b/metrics_reporter_omaha.cc
@@ -88,6 +88,10 @@
"UpdateEngine.SuccessfulUpdate.DownloadOverheadPercentage";
const char kMetricSuccessfulUpdateDownloadSourcesUsed[] =
"UpdateEngine.SuccessfulUpdate.DownloadSourcesUsed";
+const char kMetricSuccessfulUpdateDurationFromSeenDays[] =
+ "UpdateEngine.SuccessfulUpdate.DurationFromSeenDays.NoTimeRestriction";
+const char kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays[] =
+ "UpdateEngine.SuccessfulUpdate.DurationFromSeenDays.TimeRestricted";
const char kMetricSuccessfulUpdatePayloadType[] =
"UpdateEngine.SuccessfulUpdate.PayloadType";
const char kMetricSuccessfulUpdatePayloadSizeMiB[] =
@@ -617,4 +621,19 @@
metrics_lib_->SendBoolToUMA(metric, bool_value);
}
+void MetricsReporterOmaha::ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) {
+ string metric =
+ has_time_restriction_policy
+ ? metrics::kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays
+ : metrics::kMetricSuccessfulUpdateDurationFromSeenDays;
+ LOG(INFO) << "Sending " << time_to_update_days << " for metric " << metric;
+
+ metrics_lib_->SendToUMA(metric,
+ time_to_update_days,
+ 1, // min: 1 days
+ 6 * 30, // max: 6 months (approx)
+ 50); // num_buckets
+}
+
} // namespace chromeos_update_engine
diff --git a/metrics_reporter_omaha.h b/metrics_reporter_omaha.h
index 10aef86..5680dec 100644
--- a/metrics_reporter_omaha.h
+++ b/metrics_reporter_omaha.h
@@ -69,6 +69,8 @@
extern const char kMetricSuccessfulUpdateBytesDownloadedMiB[];
extern const char kMetricSuccessfulUpdateDownloadOverheadPercentage[];
extern const char kMetricSuccessfulUpdateDownloadSourcesUsed[];
+extern const char kMetricSuccessfulUpdateDurationFromSeenDays[];
+extern const char kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays[];
extern const char kMetricSuccessfulUpdatePayloadType[];
extern const char kMetricSuccessfulUpdatePayloadSizeMiB[];
extern const char kMetricSuccessfulUpdateRebootCount[];
@@ -166,6 +168,9 @@
int kernel_max_rollforward_version,
bool kernel_max_rollforward_success) override;
+ void ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) override;
+
private:
friend class MetricsReporterOmahaTest;
diff --git a/metrics_reporter_omaha_unittest.cc b/metrics_reporter_omaha_unittest.cc
index 878a323..a479028 100644
--- a/metrics_reporter_omaha_unittest.cc
+++ b/metrics_reporter_omaha_unittest.cc
@@ -500,4 +500,43 @@
kernel_max_rollforward_success);
}
+TEST_F(MetricsReporterOmahaTest, ReportEnterpriseUpdateSeenToDownloadDays) {
+ constexpr int kDaysToUpdate = 10;
+ constexpr int kMinBucket = 1;
+ constexpr int kMaxBucket = 6 * 30; // approximately 6 months
+ constexpr int kNumBuckets = 50;
+
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendToUMA(metrics::kMetricSuccessfulUpdateDurationFromSeenDays,
+ kDaysToUpdate,
+ kMinBucket,
+ kMaxBucket,
+ kNumBuckets))
+ .Times(1);
+
+ reporter_.ReportEnterpriseUpdateSeenToDownloadDays(
+ false /* has_time_restriction_policy */, kDaysToUpdate);
+}
+
+TEST_F(MetricsReporterOmahaTest,
+ ReportEnterpriseTimeRestrictedUpdateSeenToDownloadTime) {
+ const int kDaysToUpdate = 15;
+ constexpr int kMinBucket = 1;
+ constexpr int kMaxBucket = 6 * 30; // approximately 6 months
+ constexpr int kNumBuckets = 50;
+
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(
+ metrics::kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays,
+ kDaysToUpdate,
+ kMinBucket,
+ kMaxBucket,
+ kNumBuckets))
+ .Times(1);
+
+ reporter_.ReportEnterpriseUpdateSeenToDownloadDays(
+ true /* has_time_restriction_policy */, kDaysToUpdate);
+}
+
} // namespace chromeos_update_engine
diff --git a/metrics_reporter_stub.h b/metrics_reporter_stub.h
index 87023ee..25660b5 100644
--- a/metrics_reporter_stub.h
+++ b/metrics_reporter_stub.h
@@ -91,6 +91,9 @@
int kernel_max_rollforward_version,
bool kernel_max_rollforward_success) override {}
+ void ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) override {}
+
private:
DISALLOW_COPY_AND_ASSIGN(MetricsReporterStub);
};
diff --git a/metrics_utils.cc b/metrics_utils.cc
index e1aa744..070626a 100644
--- a/metrics_utils.cc
+++ b/metrics_utils.cc
@@ -251,6 +251,9 @@
case ConnectionType::kUnknown:
return metrics::ConnectionType::kUnknown;
+ case ConnectionType::kDisconnected:
+ return metrics::ConnectionType::kDisconnected;
+
case ConnectionType::kEthernet:
if (tethering == ConnectionTethering::kConfirmed)
return metrics::ConnectionType::kTetheredEthernet;
diff --git a/metrics_utils_unittest.cc b/metrics_utils_unittest.cc
index edf6bc3..417a72b 100644
--- a/metrics_utils_unittest.cc
+++ b/metrics_utils_unittest.cc
@@ -32,6 +32,9 @@
EXPECT_EQ(metrics::ConnectionType::kUnknown,
GetConnectionType(ConnectionType::kUnknown,
ConnectionTethering::kUnknown));
+ EXPECT_EQ(metrics::ConnectionType::kDisconnected,
+ GetConnectionType(ConnectionType::kDisconnected,
+ ConnectionTethering::kUnknown));
EXPECT_EQ(metrics::ConnectionType::kEthernet,
GetConnectionType(ConnectionType::kEthernet,
ConnectionTethering::kUnknown));
diff --git a/mock_metrics_reporter.h b/mock_metrics_reporter.h
index c678a80..baf3a78 100644
--- a/mock_metrics_reporter.h
+++ b/mock_metrics_reporter.h
@@ -89,6 +89,9 @@
void(int kernel_min_version,
int kernel_max_rollforward_version,
bool kernel_max_rollforward_success));
+
+ MOCK_METHOD2(ReportEnterpriseUpdateSeenToDownloadDays,
+ void(bool has_time_restriction_policy, int time_to_update_days));
};
} // namespace chromeos_update_engine
diff --git a/mock_update_attempter.h b/mock_update_attempter.h
index 6253e3d..5df5a6b 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_module_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 95dee51..9cb9b49 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -62,38 +62,62 @@
namespace chromeos_update_engine {
-// List of custom pair tags that we interpret in the Omaha Response:
-static const char* kTagDeadline = "deadline";
-static const char* kTagDisablePayloadBackoff = "DisablePayloadBackoff";
-static const char* kTagVersion = "version";
+// List of custom attributes that we interpret in the Omaha response:
+constexpr char kAttrDeadline[] = "deadline";
+constexpr char kAttrDisableP2PForDownloading[] = "DisableP2PForDownloading";
+constexpr char kAttrDisableP2PForSharing[] = "DisableP2PForSharing";
+constexpr char kAttrDisablePayloadBackoff[] = "DisablePayloadBackoff";
+constexpr char kAttrVersion[] = "version";
// Deprecated: "IsDelta"
-static const char* kTagIsDeltaPayload = "IsDeltaPayload";
-static const char* kTagMaxFailureCountPerUrl = "MaxFailureCountPerUrl";
-static const char* kTagMaxDaysToScatter = "MaxDaysToScatter";
+constexpr char kAttrIsDeltaPayload[] = "IsDeltaPayload";
+constexpr char kAttrMaxFailureCountPerUrl[] = "MaxFailureCountPerUrl";
+constexpr char kAttrMaxDaysToScatter[] = "MaxDaysToScatter";
// Deprecated: "ManifestSignatureRsa"
// Deprecated: "ManifestSize"
-static const char* kTagMetadataSignatureRsa = "MetadataSignatureRsa";
-static const char* kTagMetadataSize = "MetadataSize";
-static const char* kTagMoreInfo = "MoreInfo";
+constexpr char kAttrMetadataSignatureRsa[] = "MetadataSignatureRsa";
+constexpr char kAttrMetadataSize[] = "MetadataSize";
+constexpr char kAttrMoreInfo[] = "MoreInfo";
+constexpr char kAttrNoUpdate[] = "noupdate";
// Deprecated: "NeedsAdmin"
-static const char* kTagPrompt = "Prompt";
-static const char* kTagDisableP2PForDownloading = "DisableP2PForDownloading";
-static const char* kTagDisableP2PForSharing = "DisableP2PForSharing";
-static const char* kTagPublicKeyRsa = "PublicKeyRsa";
-static const char* kTagPowerwash = "Powerwash";
+constexpr char kAttrPollInterval[] = "PollInterval";
+constexpr char kAttrPowerwash[] = "Powerwash";
+constexpr char kAttrPrompt[] = "Prompt";
+constexpr char kAttrPublicKeyRsa[] = "PublicKeyRsa";
-static const char* kOmahaUpdaterVersion = "0.1.0.0";
+// List of attributes that we interpret in the Omaha response:
+constexpr char kAttrAppId[] = "appid";
+constexpr char kAttrCodeBase[] = "codebase";
+constexpr char kAttrCohort[] = "cohort";
+constexpr char kAttrCohortHint[] = "cohorthint";
+constexpr char kAttrCohortName[] = "cohortname";
+constexpr char kAttrElapsedDays[] = "elapsed_days";
+constexpr char kAttrElapsedSeconds[] = "elapsed_seconds";
+constexpr char kAttrEvent[] = "event";
+constexpr char kAttrHashSha256[] = "hash_sha256";
+// Deprecated: "hash"; Although we still need to pass it from the server for
+// backward compatibility.
+constexpr char kAttrName[] = "name";
+// Deprecated: "sha256"; Although we still need to pass it from the server for
+// backward compatibility.
+constexpr char kAttrSize[] = "size";
+constexpr char kAttrStatus[] = "status";
+
+// List of values that we interpret in the Omaha response:
+constexpr char kValPostInstall[] = "postinstall";
+constexpr char kValNoUpdate[] = "noupdate";
+
+constexpr char kOmahaUpdaterVersion[] = "0.1.0.0";
// X-Goog-Update headers.
-static const char* kXGoogleUpdateInteractivity = "X-Goog-Update-Interactivity";
-static const char* kXGoogleUpdateAppId = "X-Goog-Update-AppId";
-static const char* kXGoogleUpdateUpdater = "X-Goog-Update-Updater";
+constexpr char kXGoogleUpdateInteractivity[] = "X-Goog-Update-Interactivity";
+constexpr char kXGoogleUpdateAppId[] = "X-Goog-Update-AppId";
+constexpr char kXGoogleUpdateUpdater[] = "X-Goog-Update-Updater";
// updatecheck attributes (without the underscore prefix).
-static const char* kEolAttr = "eol";
-static const char* kRollback = "rollback";
-static const char* kFirmwareVersion = "firmware_version";
-static const char* kKernelVersion = "kernel_version";
+constexpr char kAttrEol[] = "eol";
+constexpr char kAttrRollback[] = "rollback";
+constexpr char kAttrFirmwareVersion[] = "firmware_version";
+constexpr char kAttrKernelVersion[] = "kernel_version";
namespace {
@@ -125,6 +149,7 @@
OmahaRequestParams* params,
bool ping_only,
bool include_ping,
+ bool skip_updatecheck,
int ping_active_days,
int ping_roll_call_days,
PrefsInterface* prefs) {
@@ -133,17 +158,20 @@
if (include_ping)
app_body = GetPingXml(ping_active_days, ping_roll_call_days);
if (!ping_only) {
- app_body += " <updatecheck";
- if (!params->target_version_prefix().empty()) {
- app_body += base::StringPrintf(
- " targetversionprefix=\"%s\"",
- XmlEncodeWithDefault(params->target_version_prefix(), "").c_str());
- // Rollback requires target_version_prefix set.
- if (params->rollback_allowed()) {
- app_body += " rollback_allowed=\"true\"";
+ if (!skip_updatecheck) {
+ app_body += " <updatecheck";
+ if (!params->target_version_prefix().empty()) {
+ app_body += base::StringPrintf(
+ " targetversionprefix=\"%s\"",
+ XmlEncodeWithDefault(params->target_version_prefix(), "")
+ .c_str());
+ // Rollback requires target_version_prefix set.
+ if (params->rollback_allowed()) {
+ app_body += " rollback_allowed=\"true\"";
+ }
}
+ app_body += "></updatecheck>\n";
}
- app_body += "></updatecheck>\n";
// If this is the first update check after a reboot following a previous
// update, generate an event containing the previous version number. If
@@ -242,12 +270,18 @@
const OmahaAppData& app_data,
bool ping_only,
bool include_ping,
+ bool skip_updatecheck,
int ping_active_days,
int ping_roll_call_days,
int install_date_in_days,
SystemState* system_state) {
- string app_body = GetAppBody(event, params, ping_only, include_ping,
- ping_active_days, ping_roll_call_days,
+ string app_body = GetAppBody(event,
+ params,
+ ping_only,
+ include_ping,
+ skip_updatecheck,
+ ping_active_days,
+ ping_roll_call_days,
system_state->prefs());
string app_versions;
@@ -378,11 +412,13 @@
.id = params->GetAppId(),
.version = params->app_version(),
.product_components = params->product_components()};
+ // Skips updatecheck for platform app in case of an install operation.
string app_xml = GetAppXml(event,
params,
product_app,
ping_only,
include_ping,
+ params->is_install(), /* skip_updatecheck */
ping_active_days,
ping_roll_call_days,
install_date_in_days,
@@ -395,6 +431,24 @@
system_app,
ping_only,
include_ping,
+ false, /* skip_updatecheck */
+ ping_active_days,
+ ping_roll_call_days,
+ install_date_in_days,
+ system_state);
+ }
+ // Create APP ID according to |dlc_module_id| (sticking the current AppID to
+ // the DLC module ID with an underscode).
+ for (const auto& dlc_module_id : params->dlc_module_ids()) {
+ OmahaAppData dlc_module_app = {
+ .id = params->GetAppId() + "_" + dlc_module_id,
+ .version = params->app_version()};
+ app_xml += GetAppXml(event,
+ params,
+ dlc_module_app,
+ ping_only,
+ include_ping,
+ false, /* skip_updatecheck */
ping_active_days,
ping_roll_call_days,
install_date_in_days,
@@ -488,27 +542,27 @@
if (data->current_path == "/response/app") {
OmahaParserData::App app;
- if (attrs.find("appid") != attrs.end()) {
- app.id = attrs["appid"];
+ if (attrs.find(kAttrAppId) != attrs.end()) {
+ app.id = attrs[kAttrAppId];
}
- if (attrs.find("cohort") != attrs.end()) {
+ if (attrs.find(kAttrCohort) != attrs.end()) {
app.cohort_set = true;
- app.cohort = attrs["cohort"];
+ app.cohort = attrs[kAttrCohort];
}
- if (attrs.find("cohorthint") != attrs.end()) {
+ if (attrs.find(kAttrCohortHint) != attrs.end()) {
app.cohorthint_set = true;
- app.cohorthint = attrs["cohorthint"];
+ app.cohorthint = attrs[kAttrCohortHint];
}
- if (attrs.find("cohortname") != attrs.end()) {
+ if (attrs.find(kAttrCohortName) != attrs.end()) {
app.cohortname_set = true;
- app.cohortname = attrs["cohortname"];
+ app.cohortname = attrs[kAttrCohortName];
}
data->apps.push_back(std::move(app));
} else if (data->current_path == "/response/app/updatecheck") {
if (!data->apps.empty())
- data->apps.back().updatecheck_status = attrs["status"];
+ data->apps.back().updatecheck_status = attrs[kAttrStatus];
if (data->updatecheck_poll_interval.empty())
- data->updatecheck_poll_interval = attrs["PollInterval"];
+ data->updatecheck_poll_interval = attrs[kAttrPollInterval];
// Omaha sends arbitrary key-value pairs as extra attributes starting with
// an underscore.
for (const auto& attr : attrs) {
@@ -517,27 +571,27 @@
}
} else if (data->current_path == "/response/daystart") {
// Get the install-date.
- data->daystart_elapsed_days = attrs["elapsed_days"];
- data->daystart_elapsed_seconds = attrs["elapsed_seconds"];
+ data->daystart_elapsed_days = attrs[kAttrElapsedDays];
+ data->daystart_elapsed_seconds = attrs[kAttrElapsedSeconds];
} else if (data->current_path == "/response/app/updatecheck/urls/url") {
// Look at all <url> elements.
if (!data->apps.empty())
- data->apps.back().url_codebase.push_back(attrs["codebase"]);
+ data->apps.back().url_codebase.push_back(attrs[kAttrCodeBase]);
} else if (data->current_path ==
"/response/app/updatecheck/manifest/packages/package") {
// Look at all <package> elements.
if (!data->apps.empty())
- data->apps.back().packages.push_back({.name = attrs["name"],
- .size = attrs["size"],
- .hash = attrs["hash_sha256"]});
+ data->apps.back().packages.push_back({.name = attrs[kAttrName],
+ .size = attrs[kAttrSize],
+ .hash = attrs[kAttrHashSha256]});
} else if (data->current_path == "/response/app/updatecheck/manifest") {
// Get the version.
if (!data->apps.empty())
- data->apps.back().manifest_version = attrs[kTagVersion];
+ data->apps.back().manifest_version = attrs[kAttrVersion];
} else if (data->current_path ==
"/response/app/updatecheck/manifest/actions/action") {
// We only care about the postinstall action.
- if (attrs["event"] == "postinstall" && !data->apps.empty()) {
+ if (attrs[kAttrEvent] == kValPostInstall && !data->apps.empty()) {
data->apps.back().action_postinstall_attrs = std::move(attrs);
}
}
@@ -852,7 +906,8 @@
bool ParsePackage(OmahaParserData::App* app,
OmahaResponse* output_object,
ScopedActionCompleter* completer) {
- if (app->updatecheck_status == "noupdate") {
+ if (app->updatecheck_status.empty() ||
+ app->updatecheck_status == kValNoUpdate) {
if (!app->packages.empty()) {
LOG(ERROR) << "No update in this <app> but <package> is not empty.";
completer->set_code(ErrorCode::kOmahaResponseInvalid);
@@ -872,17 +927,17 @@
}
LOG(INFO) << "Found " << app->url_codebase.size() << " url(s)";
vector<string> metadata_sizes =
- base::SplitString(app->action_postinstall_attrs[kTagMetadataSize],
+ base::SplitString(app->action_postinstall_attrs[kAttrMetadataSize],
":",
base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL);
- vector<string> metadata_signatures =
- base::SplitString(app->action_postinstall_attrs[kTagMetadataSignatureRsa],
- ":",
- base::TRIM_WHITESPACE,
- base::SPLIT_WANT_ALL);
+ vector<string> metadata_signatures = base::SplitString(
+ app->action_postinstall_attrs[kAttrMetadataSignatureRsa],
+ ":",
+ base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_ALL);
vector<string> is_delta_payloads =
- base::SplitString(app->action_postinstall_attrs[kTagIsDeltaPayload],
+ base::SplitString(app->action_postinstall_attrs[kAttrIsDeltaPayload],
":",
base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL);
@@ -946,11 +1001,11 @@
void ParseRollbackVersions(OmahaParserData* parser_data,
OmahaResponse* output_object) {
utils::ParseRollbackKeyVersion(
- parser_data->updatecheck_attrs[kFirmwareVersion],
+ parser_data->updatecheck_attrs[kAttrFirmwareVersion],
&output_object->rollback_key_version.firmware_key,
&output_object->rollback_key_version.firmware);
utils::ParseRollbackKeyVersion(
- parser_data->updatecheck_attrs[kKernelVersion],
+ parser_data->updatecheck_attrs[kAttrKernelVersion],
&output_object->rollback_key_version.kernel_key,
&output_object->rollback_key_version.kernel);
}
@@ -1020,7 +1075,7 @@
// Rollback-related updatecheck attributes.
// Defaults to false if attribute is not present.
output_object->is_rollback =
- ParseBool(parser_data->updatecheck_attrs[kRollback]);
+ ParseBool(parser_data->updatecheck_attrs[kAttrRollback]);
// Parses the rollback versions of the current image. If the fields do not
// exist they default to 0xffff for the 4 key versions.
@@ -1045,22 +1100,29 @@
OmahaResponse* output_object,
ScopedActionCompleter* completer) {
output_object->update_exists = false;
- for (size_t i = 0; i < parser_data->apps.size(); i++) {
- const string& status = parser_data->apps[i].updatecheck_status;
- if (status == "noupdate") {
+ for (const auto& app : parser_data->apps) {
+ const string& status = app.updatecheck_status;
+ if (status == kValNoUpdate) {
// Don't update if any app has status="noupdate".
- LOG(INFO) << "No update for <app> " << i;
+ LOG(INFO) << "No update for <app> " << app.id;
output_object->update_exists = false;
break;
} else if (status == "ok") {
- if (parser_data->apps[i].action_postinstall_attrs["noupdate"] == "true") {
+ auto const& attr_no_update =
+ app.action_postinstall_attrs.find(kAttrNoUpdate);
+ if (attr_no_update != app.action_postinstall_attrs.end() &&
+ attr_no_update->second == "true") {
// noupdate="true" in postinstall attributes means it's an update to
// self, only update if there's at least one app really have update.
- LOG(INFO) << "Update to self for <app> " << i;
+ LOG(INFO) << "Update to self for <app> " << app.id;
} else {
- LOG(INFO) << "Update for <app> " << i;
+ LOG(INFO) << "Update for <app> " << app.id;
output_object->update_exists = true;
}
+ } else if (status.empty() && params_->is_install() &&
+ params_->GetAppId() == app.id) {
+ // Skips the platform app for install operation.
+ LOG(INFO) << "No payload (and ignore) for <app> " << app.id;
} else {
LOG(ERROR) << "Unknown Omaha response status: " << status;
completer->set_code(ErrorCode::kOmahaResponseInvalid);
@@ -1088,11 +1150,20 @@
// this is the system app (this check is intentionally skipped if there is
// no system_app_id set)
output_object->system_version = app.manifest_version;
+ } else if (params_->is_install() &&
+ app.manifest_version != params_->app_version()) {
+ LOG(WARNING) << "An app has a different version (" << app.manifest_version
+ << ") that is different than platform app version ("
+ << params_->app_version() << ")";
}
if (!app.action_postinstall_attrs.empty() && attrs.empty()) {
attrs = app.action_postinstall_attrs;
}
}
+ if (params_->is_install()) {
+ LOG(INFO) << "Use request version for Install operation.";
+ output_object->version = params_->app_version();
+ }
if (output_object->version.empty()) {
LOG(ERROR) << "Omaha Response does not have version in manifest!";
completer->set_code(ErrorCode::kOmahaResponseInvalid);
@@ -1109,23 +1180,23 @@
}
// Get the optional properties one by one.
- output_object->more_info_url = attrs[kTagMoreInfo];
- output_object->prompt = ParseBool(attrs[kTagPrompt]);
- output_object->deadline = attrs[kTagDeadline];
- output_object->max_days_to_scatter = ParseInt(attrs[kTagMaxDaysToScatter]);
+ output_object->more_info_url = attrs[kAttrMoreInfo];
+ output_object->prompt = ParseBool(attrs[kAttrPrompt]);
+ output_object->deadline = attrs[kAttrDeadline];
+ output_object->max_days_to_scatter = ParseInt(attrs[kAttrMaxDaysToScatter]);
output_object->disable_p2p_for_downloading =
- ParseBool(attrs[kTagDisableP2PForDownloading]);
+ ParseBool(attrs[kAttrDisableP2PForDownloading]);
output_object->disable_p2p_for_sharing =
- ParseBool(attrs[kTagDisableP2PForSharing]);
- output_object->public_key_rsa = attrs[kTagPublicKeyRsa];
+ ParseBool(attrs[kAttrDisableP2PForSharing]);
+ output_object->public_key_rsa = attrs[kAttrPublicKeyRsa];
- string max = attrs[kTagMaxFailureCountPerUrl];
+ string max = attrs[kAttrMaxFailureCountPerUrl];
if (!base::StringToUint(max, &output_object->max_failure_count_per_url))
output_object->max_failure_count_per_url = kDefaultMaxFailureCountPerUrl;
output_object->disable_payload_backoff =
- ParseBool(attrs[kTagDisablePayloadBackoff]);
- output_object->powerwash_required = ParseBool(attrs[kTagPowerwash]);
+ ParseBool(attrs[kAttrDisablePayloadBackoff]);
+ output_object->powerwash_required = ParseBool(attrs[kAttrPowerwash]);
return true;
}
@@ -1223,14 +1294,17 @@
output_object.update_exists = true;
SetOutputObject(output_object);
+ LoadOrPersistUpdateFirstSeenAtPref();
+
ErrorCode error = ErrorCode::kSuccess;
- if (ShouldIgnoreUpdate(&error, output_object)) {
+ if (ShouldIgnoreUpdate(output_object, &error)) {
// No need to change output_object.update_exists here, since the value
// has been output to the pipe.
completer.set_code(error);
return;
}
+
// If Omaha says to disable p2p, respect that
if (output_object.disable_p2p_for_downloading) {
LOG(INFO) << "Forcibly disabling use of p2p for downloading as "
@@ -1283,17 +1357,6 @@
OmahaResponse& output_object = const_cast<OmahaResponse&>(GetOutputObject());
PayloadStateInterface* payload_state = system_state_->payload_state();
- if (system_state_->hardware()->IsOOBEEnabled() &&
- !system_state_->hardware()->IsOOBEComplete(nullptr) &&
- (output_object.deadline.empty() ||
- payload_state->GetRollbackHappened()) &&
- params_->app_version() != "ForcedUpdate") {
- output_object.update_exists = false;
- LOG(INFO) << "Ignoring non-critical Omaha updates until OOBE is done.";
- completer.set_code(ErrorCode::kNonCriticalUpdateInOOBE);
- return;
- }
-
if (ShouldDeferDownload(&output_object)) {
output_object.update_exists = false;
LOG(INFO) << "Ignoring Omaha updates as updates are deferred by policy.";
@@ -1435,47 +1498,11 @@
OmahaRequestAction::WallClockWaitResult
OmahaRequestAction::IsWallClockBasedWaitingSatisfied(
OmahaResponse* output_object) {
- Time update_first_seen_at;
- int64_t update_first_seen_at_int;
-
- if (system_state_->prefs()->Exists(kPrefsUpdateFirstSeenAt)) {
- if (system_state_->prefs()->GetInt64(kPrefsUpdateFirstSeenAt,
- &update_first_seen_at_int)) {
- // Note: This timestamp could be that of ANY update we saw in the past
- // (not necessarily this particular update we're considering to apply)
- // but never got to apply because of some reason (e.g. stop AU policy,
- // updates being pulled out from Omaha, changes in target version prefix,
- // new update being rolled out, etc.). But for the purposes of scattering
- // it doesn't matter which update the timestamp corresponds to. i.e.
- // the clock starts ticking the first time we see an update and we're
- // ready to apply when the random wait period is satisfied relative to
- // that first seen timestamp.
- update_first_seen_at = Time::FromInternalValue(update_first_seen_at_int);
- LOG(INFO) << "Using persisted value of UpdateFirstSeenAt: "
- << utils::ToString(update_first_seen_at);
- } else {
- // This seems like an unexpected error where the persisted value exists
- // but it's not readable for some reason. Just skip scattering in this
- // case to be safe.
- LOG(INFO) << "Not scattering as UpdateFirstSeenAt value cannot be read";
- return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
- }
- } else {
- update_first_seen_at = system_state_->clock()->GetWallclockTime();
- update_first_seen_at_int = update_first_seen_at.ToInternalValue();
- if (system_state_->prefs()->SetInt64(kPrefsUpdateFirstSeenAt,
- update_first_seen_at_int)) {
- LOG(INFO) << "Persisted the new value for UpdateFirstSeenAt: "
- << utils::ToString(update_first_seen_at);
- } else {
- // This seems like an unexpected error where the value cannot be
- // persisted for some reason. Just skip scattering in this
- // case to be safe.
- LOG(INFO) << "Not scattering as UpdateFirstSeenAt value "
- << utils::ToString(update_first_seen_at)
- << " cannot be persisted";
- return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
- }
+ Time update_first_seen_at = LoadOrPersistUpdateFirstSeenAtPref();
+ if (update_first_seen_at == base::Time()) {
+ LOG(INFO) << "Not scattering as UpdateFirstSeenAt value cannot be read or "
+ "persisted";
+ return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
}
TimeDelta elapsed_time =
@@ -1654,7 +1681,7 @@
}
bool OmahaRequestAction::PersistEolStatus(const map<string, string>& attrs) {
- auto eol_attr = attrs.find(kEolAttr);
+ auto eol_attr = attrs.find(kAttrEol);
if (eol_attr != attrs.end()) {
return system_state_->prefs()->SetString(kPrefsOmahaEolStatus,
eol_attr->second);
@@ -1727,8 +1754,8 @@
system_state_, result, reaction, download_error_code);
}
-bool OmahaRequestAction::ShouldIgnoreUpdate(
- ErrorCode* error, const OmahaResponse& response) const {
+bool OmahaRequestAction::ShouldIgnoreUpdate(const OmahaResponse& response,
+ ErrorCode* error) const {
// Note: policy decision to not update to a version we rolled back from.
string rollback_version =
system_state_->payload_state()->GetRollbackVersion();
@@ -1741,6 +1768,16 @@
}
}
+ if (system_state_->hardware()->IsOOBEEnabled() &&
+ !system_state_->hardware()->IsOOBEComplete(nullptr) &&
+ (response.deadline.empty() ||
+ system_state_->payload_state()->GetRollbackHappened()) &&
+ params_->app_version() != "ForcedUpdate") {
+ LOG(INFO) << "Ignoring a non-critical Omaha update before OOBE completion.";
+ *error = ErrorCode::kNonCriticalUpdateInOOBE;
+ return true;
+ }
+
if (!IsUpdateAllowedOverCurrentConnection(error, response)) {
LOG(INFO) << "Update is not allowed over current connection.";
return true;
@@ -1914,4 +1951,47 @@
min_kernel_version, max_kernel_rollforward, max_rollforward_set);
}
+base::Time OmahaRequestAction::LoadOrPersistUpdateFirstSeenAtPref() const {
+ Time update_first_seen_at;
+ int64_t update_first_seen_at_int;
+ if (system_state_->prefs()->Exists(kPrefsUpdateFirstSeenAt)) {
+ if (system_state_->prefs()->GetInt64(kPrefsUpdateFirstSeenAt,
+ &update_first_seen_at_int)) {
+ // Note: This timestamp could be that of ANY update we saw in the past
+ // (not necessarily this particular update we're considering to apply)
+ // but never got to apply because of some reason (e.g. stop AU policy,
+ // updates being pulled out from Omaha, changes in target version prefix,
+ // new update being rolled out, etc.). But for the purposes of scattering
+ // it doesn't matter which update the timestamp corresponds to. i.e.
+ // the clock starts ticking the first time we see an update and we're
+ // ready to apply when the random wait period is satisfied relative to
+ // that first seen timestamp.
+ update_first_seen_at = Time::FromInternalValue(update_first_seen_at_int);
+ LOG(INFO) << "Using persisted value of UpdateFirstSeenAt: "
+ << utils::ToString(update_first_seen_at);
+ } else {
+ // This seems like an unexpected error where the persisted value exists
+ // but it's not readable for some reason.
+ LOG(INFO) << "UpdateFirstSeenAt value cannot be read";
+ return base::Time();
+ }
+ } else {
+ update_first_seen_at = system_state_->clock()->GetWallclockTime();
+ update_first_seen_at_int = update_first_seen_at.ToInternalValue();
+ if (system_state_->prefs()->SetInt64(kPrefsUpdateFirstSeenAt,
+ update_first_seen_at_int)) {
+ LOG(INFO) << "Persisted the new value for UpdateFirstSeenAt: "
+ << utils::ToString(update_first_seen_at);
+ } else {
+ // This seems like an unexpected error where the value cannot be
+ // persisted for some reason.
+ LOG(INFO) << "UpdateFirstSeenAt value "
+ << utils::ToString(update_first_seen_at)
+ << " cannot be persisted";
+ return base::Time();
+ }
+ }
+ return update_first_seen_at;
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_request_action.h b/omaha_request_action.h
index 1034c3f..c083abe 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -303,8 +303,8 @@
void OnLookupPayloadViaP2PCompleted(const std::string& url);
// Returns true if the current update should be ignored.
- bool ShouldIgnoreUpdate(ErrorCode* error,
- const OmahaResponse& response) const;
+ bool ShouldIgnoreUpdate(const OmahaResponse& response,
+ ErrorCode* error) const;
// Return true if updates are allowed by user preferences.
bool IsUpdateAllowedOverCellularByPrefs(const OmahaResponse& response) const;
@@ -322,6 +322,11 @@
// enabled.
void SetMaxKernelKeyVersionForRollback() const;
+ // Reads and returns the kPrefsUpdateFirstSeenAt pref if the pref currently
+ // exists. Otherwise saves the current wallclock time to the
+ // kPrefsUpdateFirstSeenAt pref and returns it as a base::Time object.
+ base::Time LoadOrPersistUpdateFirstSeenAtPref() const;
+
// Global system context.
SystemState* system_state_;
diff --git a/omaha_request_action_fuzzer.cc b/omaha_request_action_fuzzer.cc
new file mode 100644
index 0000000..6c2f7ca
--- /dev/null
+++ b/omaha_request_action_fuzzer.cc
@@ -0,0 +1,53 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <brillo/message_loops/fake_message_loop.h>
+
+#include "update_engine/common/mock_http_fetcher.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/fake_system_state.h"
+#include "update_engine/omaha_request_action.h"
+
+class Environment {
+ public:
+ Environment() { logging::SetMinLogLevel(logging::LOG_FATAL); }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static Environment env;
+ brillo::FakeMessageLoop loop(nullptr);
+ loop.SetAsCurrent();
+
+ chromeos_update_engine::FakeSystemState fake_system_state;
+ auto omaha_request_action =
+ std::make_unique<chromeos_update_engine::OmahaRequestAction>(
+ &fake_system_state,
+ nullptr,
+ std::make_unique<chromeos_update_engine::MockHttpFetcher>(
+ data, size, nullptr),
+ false);
+ auto collector_action =
+ std::make_unique<chromeos_update_engine::ObjectCollectorAction<
+ chromeos_update_engine::OmahaResponse>>();
+ BondActions(omaha_request_action.get(), collector_action.get());
+ chromeos_update_engine::ActionProcessor action_processor;
+ action_processor.EnqueueAction(std::move(omaha_request_action));
+ action_processor.EnqueueAction(std::move(collector_action));
+ action_processor.StartProcessing();
+
+ loop.Run();
+ return 0;
+}
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 019b723..9a5cd10 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -77,8 +77,10 @@
"Don't change the value of kRollforward infinity unless its "
"size has been changed in firmware.");
+const char kCurrentVersion[] = "0.1.0.0";
const char kTestAppId[] = "test-app-id";
const char kTestAppId2[] = "test-app2-id";
+const char kTestAppIdSkipUpdatecheck[] = "test-app-id-skip-updatecheck";
// This is a helper struct to allow unit tests build an update response with the
// values they care about.
@@ -145,19 +147,16 @@
: "") +
"</packages>"
"<actions><action event=\"postinstall\" MetadataSize=\"11" +
- (multi_package ? ":22" : "") + "\" ChromeOSVersion=\"" + version +
- "\" MoreInfo=\"" + more_info_url + "\" Prompt=\"" + prompt +
+ (multi_package ? ":22" : "") + "\" MoreInfo=\"" + more_info_url +
+ "\" Prompt=\"" + prompt +
"\" "
- "IsDelta=\"true\" "
"IsDeltaPayload=\"true" +
(multi_package ? ":false" : "") +
"\" "
"MaxDaysToScatter=\"" +
max_days_to_scatter +
"\" "
- "sha256=\"not-used\" "
- "needsadmin=\"" +
- needsadmin + "\" " +
+ "sha256=\"not-used\" " +
(deadline.empty() ? "" : ("deadline=\"" + deadline + "\" ")) +
(disable_p2p_for_downloading ? "DisableP2PForDownloading=\"true\" "
: "") +
@@ -182,6 +181,9 @@
(multi_app_no_update
? "<app><updatecheck status=\"noupdate\"/></app>"
: "") +
+ (multi_app_skip_updatecheck
+ ? "<app appid=\"" + app_id_skip_updatecheck + "\"></app>"
+ : "") +
"</response>";
}
@@ -192,6 +194,8 @@
string app_id = kTestAppId;
string app_id2 = kTestAppId2;
+ string app_id_skip_updatecheck = kTestAppIdSkipUpdatecheck;
+ string current_version = kCurrentVersion;
string version = "1.2.3.4";
string version2 = "2.3.4.5";
string more_info_url = "http://more/info";
@@ -200,7 +204,6 @@
string codebase2 = "http://code/base/2/";
string filename = "file.signed";
string hash = "4841534831323334";
- string needsadmin = "false";
uint64_t size = 123;
string deadline = "";
string max_days_to_scatter = "7";
@@ -227,6 +230,8 @@
bool multi_app_self_update = false;
// Whether to include an additional app with status="noupdate".
bool multi_app_no_update = false;
+ // Whether to include an additional app with no updatecheck tag.
+ bool multi_app_skip_updatecheck = false;
// Whether to include more than one package in an app.
bool multi_package = false;
@@ -297,7 +302,7 @@
request_params_.set_os_sp("service_pack");
request_params_.set_os_board("x86-generic");
request_params_.set_app_id(kTestAppId);
- request_params_.set_app_version("0.1.0.0");
+ request_params_.set_app_version(kCurrentVersion);
request_params_.set_app_lang("en-US");
request_params_.set_current_channel("unittest");
request_params_.set_target_channel("unittest");
@@ -310,6 +315,8 @@
request_params_.set_target_version_prefix("");
request_params_.set_rollback_allowed(false);
request_params_.set_is_powerwash_allowed(false);
+ request_params_.set_is_install(false);
+ request_params_.set_dlc_module_ids({});
fake_system_state_.set_request_params(&request_params_);
fake_system_state_.set_prefs(&fake_prefs_);
@@ -1157,12 +1164,45 @@
EXPECT_FALSE(response.update_exists);
}
+// Verify that non-critical updates are skipped by reporting the
+// kNonCriticalUpdateInOOBE error code when attempted over cellular network -
+// i.e. when the update would need user permission. Note that reporting
+// kOmahaUpdateIgnoredOverCellular error in this case might cause undesired UX
+// in OOBE (warning the user about an update that will be skipped).
+TEST_F(OmahaRequestActionTest, SkipNonCriticalUpdatesInOOBEOverCellular) {
+ OmahaResponse response;
+ fake_system_state_.fake_hardware()->UnsetIsOOBEComplete();
+
+ MockConnectionManager mock_cm;
+ fake_system_state_.set_connection_manager(&mock_cm);
+
+ EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(ConnectionType::kCellular),
+ SetArgPointee<1>(ConnectionTethering::kUnknown),
+ Return(true)));
+ EXPECT_CALL(mock_cm, IsAllowedConnectionTypesForUpdateSet())
+ .WillRepeatedly(Return(false));
+
+ ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kNonCriticalUpdateInOOBE,
+ metrics::CheckResult::kParsingError,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_FALSE(response.update_exists);
+}
+
TEST_F(OmahaRequestActionTest, WallClockBasedWaitAloneCausesScattering) {
OmahaResponse response;
request_params_.set_wall_clock_based_wait_enabled(true);
request_params_.set_update_check_count_wait_enabled(false);
request_params_.set_waiting_period(TimeDelta::FromDays(2));
+ fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
-1,
false, // ping_only
@@ -1238,6 +1278,8 @@
request_params_.set_min_update_checks_needed(0);
request_params_.set_max_update_checks_allowed(0);
+ fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
ASSERT_TRUE(TestUpdateCheck(
fake_update_response_.GetUpdateResponse(),
-1,
@@ -1263,6 +1305,8 @@
request_params_.set_min_update_checks_needed(1);
request_params_.set_max_update_checks_allowed(8);
+ fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
ASSERT_FALSE(TestUpdateCheck(
fake_update_response_.GetUpdateResponse(),
-1,
@@ -1301,6 +1345,8 @@
request_params_.set_min_update_checks_needed(1);
request_params_.set_max_update_checks_allowed(8);
+ fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
ASSERT_TRUE(fake_prefs_.SetInt64(kPrefsUpdateCheckCount, 5));
ASSERT_FALSE(TestUpdateCheck(
@@ -1343,6 +1389,8 @@
request_params_.set_waiting_period(TimeDelta::FromDays(6));
request_params_.set_update_check_count_wait_enabled(false);
+ fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
ASSERT_TRUE(fake_prefs_.SetInt64(kPrefsWallClockStagingWaitPeriod, 6));
// This should not prevent scattering due to staging.
fake_update_response_.max_days_to_scatter = "0";
@@ -1631,12 +1679,9 @@
"<packages><package hash=\"not-used\" name=\"f\" "
"size=\"587\" hash_sha256=\"lkq34j5345\"/></packages>"
"<actions><action event=\"postinstall\" "
- "ChromeOSVersion=\"10.2.3.4\" "
"Prompt=\"false\" "
- "IsDelta=\"true\" "
"IsDeltaPayload=\"false\" "
"sha256=\"not-used\" "
- "needsadmin=\"true\" "
"/></actions></manifest></updatecheck></app></response>";
LOG(INFO) << "Input Response = " << input_response;
@@ -2502,7 +2547,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}\" "
@@ -3007,4 +3052,108 @@
EXPECT_EQ(4, response.rollback_key_version.kernel);
}
+TEST_F(OmahaRequestActionTest,
+ TestUpdateFirstSeenAtPrefPersistedIfUpdateExists) {
+ FakeClock fake_clock;
+ Time now = Time::Now();
+ fake_clock.SetWallclockTime(now);
+ fake_system_state_.set_clock(&fake_clock);
+
+ OmahaResponse response;
+ ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_TRUE(response.update_exists);
+ EXPECT_TRUE(fake_prefs_.Exists(kPrefsUpdateFirstSeenAt));
+
+ int64_t stored_first_seen_at_time;
+ EXPECT_TRUE(fake_prefs_.GetInt64(kPrefsUpdateFirstSeenAt,
+ &stored_first_seen_at_time));
+ EXPECT_EQ(now.ToInternalValue(), stored_first_seen_at_time);
+}
+
+TEST_F(OmahaRequestActionTest,
+ TestUpdateFirstSeenAtPrefNotPersistedIfUpdateFails) {
+ FakeClock fake_clock;
+ Time now = Time::Now();
+ fake_clock.SetWallclockTime(now);
+ fake_system_state_.set_clock(&fake_clock);
+
+ OmahaResponse response;
+ ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_FALSE(response.update_exists);
+ EXPECT_FALSE(fake_prefs_.Exists(kPrefsUpdateFirstSeenAt));
+}
+
+TEST_F(OmahaRequestActionTest, InstallTest) {
+ OmahaResponse response;
+ request_params_.set_is_install(true);
+ request_params_.set_dlc_module_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_module_id : request_params_.dlc_module_ids()) {
+ EXPECT_NE(string::npos,
+ post_str.find("appid=\"" + fake_update_response_.app_id + "_" +
+ dlc_module_id + "\""));
+ }
+ EXPECT_NE(string::npos,
+ post_str.find("appid=\"" + fake_update_response_.app_id + "\""));
+
+ // Count number of updatecheck tag in response.
+ int updatecheck_count = 0;
+ size_t pos = 0;
+ while ((pos = post_str.find("<updatecheck", pos)) != string::npos) {
+ updatecheck_count++;
+ pos++;
+ }
+ EXPECT_EQ(request_params_.dlc_module_ids().size(), updatecheck_count);
+}
+
+TEST_F(OmahaRequestActionTest, InstallMissingPlatformVersionTest) {
+ fake_update_response_.multi_app_skip_updatecheck = true;
+ fake_update_response_.multi_app_no_update = false;
+ request_params_.set_is_install(true);
+ request_params_.set_dlc_module_ids({"dlc_no_0", "dlc_no_1"});
+ request_params_.set_app_id(fake_update_response_.app_id_skip_updatecheck);
+ OmahaResponse response;
+ ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_TRUE(response.update_exists);
+ EXPECT_EQ(fake_update_response_.current_version, response.version);
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_request_params.cc b/omaha_request_params.cc
index 186ea61..c458424 100644
--- a/omaha_request_params.cc
+++ b/omaha_request_params.cc
@@ -122,6 +122,10 @@
// Set the interactive flag accordingly.
interactive_ = in_interactive;
+
+ dlc_module_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..1bfa471 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,15 @@
inline int64_t max_update_checks_allowed() const {
return max_update_checks_allowed_;
}
+ inline void set_dlc_module_ids(
+ const std::vector<std::string>& dlc_module_ids) {
+ dlc_module_ids_ = dlc_module_ids;
+ }
+ inline std::vector<std::string> dlc_module_ids() const {
+ return dlc_module_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 +339,14 @@
// When reading files, prepend root_ to the paths. Useful for testing.
std::string root_;
+ // A list of DLC module IDs to install.
+ std::vector<std::string> dlc_module_ids_;
+
+ // This variable defines whether the payload is being installed in the current
+ // partition. At the moment, this is used for installing DLC modules 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 c1fe854..ab41b84 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -121,8 +121,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 e4d05f4..b128b27 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));
@@ -284,6 +287,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/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 1bfdf9c..7dcb5f7 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -71,6 +71,7 @@
const unsigned DeltaPerformer::kProgressLogTimeoutSeconds = 30;
const unsigned DeltaPerformer::kProgressDownloadWeight = 50;
const unsigned DeltaPerformer::kProgressOperationsWeight = 50;
+const uint64_t DeltaPerformer::kCheckpointFrequencySeconds = 1;
namespace {
const int kUpdateStateOperationInvalid = -1;
@@ -1901,6 +1902,13 @@
}
bool DeltaPerformer::CheckpointUpdateProgress() {
+ base::Time curr_time = base::Time::Now();
+ if (curr_time > update_checkpoint_time_) {
+ update_checkpoint_time_ = curr_time + update_checkpoint_wait_;
+ } else {
+ return false;
+ }
+
Terminator::set_exit_blocked(true);
if (last_updated_buffer_offset_ != buffer_offset_) {
// Resets the progress in case we die in the middle of the state update.
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index cddfef4..8597a37 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -60,6 +60,7 @@
// operations. They must add up to one hundred (100).
static const unsigned kProgressDownloadWeight;
static const unsigned kProgressOperationsWeight;
+ static const uint64_t kCheckpointFrequencySeconds;
DeltaPerformer(PrefsInterface* prefs,
BootControlInterface* boot_control,
@@ -400,6 +401,12 @@
base::TimeDelta::FromSeconds(kProgressLogTimeoutSeconds)};
base::Time forced_progress_log_time_;
+ // The frequency that we should write an update checkpoint (constant), and
+ // the point in time at which the next checkpoint should be written.
+ const base::TimeDelta update_checkpoint_wait_{
+ base::TimeDelta::FromSeconds(kCheckpointFrequencySeconds)};
+ base::Time update_checkpoint_time_;
+
DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
};
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index a7ec6da..05486c5 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -353,7 +353,7 @@
DEFINE_uint64(rootfs_partition_size,
chromeos_update_engine::kRootFSPartitionSize,
"RootFS partition size for the image once installed");
- DEFINE_uint64(major_version, 1,
+ DEFINE_uint64(major_version, 2,
"The major version of the payload being generated.");
DEFINE_int32(minor_version, -1,
"The minor version of the payload being generated "
diff --git a/payload_state.cc b/payload_state.cc
index 36f120a..ab7912e 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -55,6 +55,9 @@
// We want to randomize retry attempts after the backoff by +/- 6 hours.
static const uint32_t kMaxBackoffFuzzMinutes = 12 * 60;
+// Limit persisting current update duration uptime to once per second
+static const uint64_t kUptimeResolution = 1;
+
PayloadState::PayloadState()
: prefs_(nullptr),
using_p2p_for_downloading_(false),
@@ -1153,9 +1156,12 @@
void PayloadState::CalculateUpdateDurationUptime() {
Time now = system_state_->clock()->GetMonotonicTime();
TimeDelta uptime_since_last_update = now - update_duration_uptime_timestamp_;
- TimeDelta new_uptime = update_duration_uptime_ + uptime_since_last_update;
- // We're frequently called so avoid logging this write
- SetUpdateDurationUptimeExtended(new_uptime, now, false);
+
+ if (uptime_since_last_update > TimeDelta::FromSeconds(kUptimeResolution)) {
+ TimeDelta new_uptime = update_duration_uptime_ + uptime_since_last_update;
+ // We're frequently called so avoid logging this write
+ SetUpdateDurationUptimeExtended(new_uptime, now, false);
+ }
}
string PayloadState::GetPrefsKey(const string& prefix, DownloadSource source) {
diff --git a/real_system_state.cc b/real_system_state.cc
index 9dab3a1..f576c6c 100644
--- a/real_system_state.cc
+++ b/real_system_state.cc
@@ -32,6 +32,7 @@
#include "update_engine/common/boot_control.h"
#include "update_engine/common/boot_control_stub.h"
#include "update_engine/common/constants.h"
+#include "update_engine/common/dlcservice.h"
#include "update_engine/common/hardware.h"
#include "update_engine/common/utils.h"
#include "update_engine/metrics_reporter_omaha.h"
@@ -88,6 +89,12 @@
return false;
}
+ dlcservice_ = CreateDlcService();
+ if (!dlcservice_) {
+ LOG(ERROR) << "Error initializing the DlcServiceInterface.";
+ return false;
+ }
+
// Initialize standard and powerwash-safe prefs.
base::FilePath non_volatile_path;
// TODO(deymo): Fall back to in-memory prefs if there's no physical directory
diff --git a/real_system_state.h b/real_system_state.h
index 5239160..4712008 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -31,6 +31,7 @@
#include "update_engine/certificate_checker.h"
#include "update_engine/common/boot_control_interface.h"
#include "update_engine/common/clock.h"
+#include "update_engine/common/dlcservice_interface.h"
#include "update_engine/common/hardware_interface.h"
#include "update_engine/common/prefs.h"
#include "update_engine/connection_manager_interface.h"
@@ -126,6 +127,10 @@
inline bool system_rebooted() override { return system_rebooted_; }
+ inline DlcServiceInterface* dlcservice() override {
+ return dlcservice_.get();
+ }
+
private:
// Real DBus proxies using the DBus connection.
#if USE_CHROME_KIOSK_APP
@@ -136,6 +141,9 @@
// Interface for the power manager.
std::unique_ptr<PowerManagerInterface> power_manager_;
+ // Interface for dlcservice.
+ std::unique_ptr<DlcServiceInterface> dlcservice_;
+
// Interface for the clock.
std::unique_ptr<BootControlInterface> boot_control_;
diff --git a/sample_omaha_v3_response.xml b/sample_omaha_v3_response.xml
index abba523..1aec1f2 100644
--- a/sample_omaha_v3_response.xml
+++ b/sample_omaha_v3_response.xml
@@ -1,18 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
<response protocol="3.0" server="prod">
- <daystart elapsed_seconds="56652"/>
- <app appid="{90f229ce-83e2-4faf-8479-e368a34938b1}" status="ok">
+ <daystart elapsed_days="4086" elapsed_seconds="62499"/>
+ <app appid="{C166AF52-7EE9-4F08-AAA7-B4B895A9F336}" cohort="1:3:" cohortname="caroline_beta" status="ok">
+ <ping status="ok"/>
<updatecheck status="ok">
<urls>
- <url codebase="https://storage.googleapis.com/chromeos-releases-public/canary-channel/canary-channel/3095.0.0/"/>
+ <url codebase="http://dl.google.com/chromeos/caroline/10323.52.0/beta-channel/"/>
+ <url codebase="https://dl.google.com/chromeos/caroline/10323.52.0/beta-channel/"/>
</urls>
- <manifest version="3095.0.0">
- <packages>
- <package hash="HVOmp67vBjPdvpWmOC2Uw4UDwsc=" name="chromeos_3095.0.0_x86-zgb_canary-channel_full_mp-v2.bin-df37843370ddf1e3819a2afeaa934faa.signed" required="true" size="400752559"/>
- </packages>
+ <manifest version="10323.52.0">
<actions>
- <action event="update" run="chromeos_3095.0.0_x86-zgb_canary-channel_full_mp-v2.bin-df37843370ddf1e3819a2afeaa934faa.signed"/>
- <action ChromeOSVersion="3095.0.0" ChromeVersion="24.0.1307.0" IsDelta="true" IsDeltaPayload="false" MaxDaysToScatter="14" MetadataSignatureRsa="xXrO/LahHlKk3YmqEf1qE0PN587Sc2IJV+FN7J7x1h49waNQIy/QwYO4LaOySgETe5JZXtkAEzzqakfJwxQ2pVfzj1GkExwjd5LTn1He2GvA73B8fKbS4bfP7dbUFwD5039xCwf1U2gezFViOiOPiVURx/pEsdhv+Cqx/3HbjIuj5au2dooSyDxLC5AnODzAKyYfAcjMuiLON+9SqmctJW+VjzdY9SbJAnkH2qqVjFyBKAXsYT+hOTIJ3MJpg8OSVxMMtGB99PxbOJ52F37d2Y5Fws/AUkNnNEsan/WRJA1kuWoS6rpeR8JQYuVhLiK2u/KpOcvMVRw3Q2VUxtcAGw==" MetadataSize="58315" event="postinstall" sha256="DIAVxoI+8NpsudUawOA5U92VHlaxQBS3ejN4EPM6T2A="/>
+ <action event="update" run="chromeos_10323.46.0-10323.52.0_caroline_beta-channel_delta_mp.bin-f5c4e5e263c4c119d7d22e0f18a586e5.signed"/>
+ <action ChromeOSVersion="10323.52.0" ChromeVersion="65.0.3325.148" IsDelta="true" IsDeltaPayload="true" MaxDaysToScatter="14" MetadataSignatureRsa="tkrOiIQn2GMQzjLckjiiOyuyV+RqupNW50t6JlFWOhAzWM8dm1qrJVYTYlULxTVlx4BHijbNuX7+OYk6zhRuxuceY7sUwrCM2yxERZ/sDLA5wF0u/8KLP7qrDKL2OIk9JJhF0EdLPylUAEt6vWW4pbYRFhK0girgWIPSdqdjkfHNTKWEUtcQ3iAAB8AvLNOyGP/en0makFvSVXZ8Mq95UrSwWMYFdVmWdVkyRtLYSwLaz5J45y3DQuk3YjeaHhRlH/AQ3OJXX6rjTCwgyiddAccOalwFVwrczq6AUs5S+/vWAMqi+7YfCPgjRdPPIhRJVKcIiAPb8RNXlP+rigGwew==" MetadataSize="414487" event="postinstall" sha256="kONiEAWQV7UyBjOoFBcKDz0OkUx0yRuIGzse4O6rmDs="/>
</actions>
+ <packages>
+ <package fp="1.90e36210059057b5320633a814170a0f3d0e914c74c91b881b3b1ee0eeab983b" hash="gD1W+dPZiNEhz3f3odCtfL81Yi8=" hash_sha256="90e36210059057b5320633a814170a0f3d0e914c74c91b881b3b1ee0eeab983b" name="chromeos_10323.46.0-10323.52.0_caroline_beta-channel_delta_mp.bin-f5c4e5e263c4c119d7d22e0f18a586e5.signed" required="true" size="29981022"/>
+ </packages>
</manifest>
</updatecheck>
</app>
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index 5c6e6d7..c88709c 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -90,7 +90,7 @@
load_shflags() {
local my_dir="$(dirname "$(readlink -f "$0")")"
local path
- for path in /usr/share/misc {/usr/lib/crosutils,"${my_dir}"}/lib/shflags; do
+ for path in /usr/share/misc "${my_dir}"/lib/shflags; do
if [[ -r "${path}/shflags" ]]; then
. "${path}/shflags" || die "Could not load ${path}/shflags."
return
@@ -371,12 +371,11 @@
cros_generate_update_payload --extract \
--image "${image}" \
- --kern_path "${kernel}" --root_path "${root}" \
- --work_dir "${FLAGS_work_dir}" --outside_chroot
+ --kern_path "${kernel}" --root_path "${root}"
- # Chrome OS uses major_version 1 payloads for all versions, even if the
- # updater supports a newer major version.
- FORCE_MAJOR_VERSION="1"
+ # Chrome OS now uses major_version 2 payloads for all boards.
+ # See crbug.com/794404 for more information.
+ FORCE_MAJOR_VERSION="2"
eval ${partitions_array}[kernel]=\""${kernel}"\"
eval ${partitions_array}[root]=\""${root}"\"
diff --git a/scripts/update_payload/checker.py b/scripts/update_payload/checker.py
index 746d4be..15f11ae 100644
--- a/scripts/update_payload/checker.py
+++ b/scripts/update_payload/checker.py
@@ -627,7 +627,7 @@
self._CheckPresentIff(self.sigs_offset, self.sigs_size,
'signatures_offset', 'signatures_size', 'manifest')
- if self.major_version == 1:
+ if self.major_version == common.CHROMEOS_MAJOR_PAYLOAD_VERSION:
for real_name, proto_name in common.CROS_PARTITIONS:
self.old_part_info[real_name] = self._CheckOptionalSubMsg(
manifest, 'old_%s_info' % proto_name, report)
@@ -1069,8 +1069,9 @@
# Type-specific checks.
if op.type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
self._CheckReplaceOperation(op, data_length, total_dst_blocks, op_name)
- elif op.type == common.OpType.REPLACE_XZ and (self.minor_version >= 3 or
- self.major_version >= 2):
+ elif (op.type == common.OpType.REPLACE_XZ and
+ (self.minor_version >= 3 or
+ self.major_version >= common.BRILLO_MAJOR_PAYLOAD_VERSION)):
self._CheckReplaceOperation(op, data_length, total_dst_blocks, op_name)
elif op.type == common.OpType.MOVE and self.minor_version == 1:
self._CheckMoveOperation(op, data_offset, total_src_blocks,
@@ -1251,16 +1252,19 @@
last_ops_section = (self.payload.manifest.kernel_install_operations or
self.payload.manifest.install_operations)
- fake_sig_op = last_ops_section[-1]
- # Check: signatures_{offset,size} must match the last (fake) operation.
- if not (fake_sig_op.type == common.OpType.REPLACE and
- self.sigs_offset == fake_sig_op.data_offset and
- self.sigs_size == fake_sig_op.data_length):
- raise error.PayloadError(
- 'Signatures_{offset,size} (%d+%d) does not match last operation '
- '(%d+%d).' %
- (self.sigs_offset, self.sigs_size, fake_sig_op.data_offset,
- fake_sig_op.data_length))
+
+ # Only major version 1 has the fake signature OP at the end.
+ if self.major_version == common.CHROMEOS_MAJOR_PAYLOAD_VERSION:
+ fake_sig_op = last_ops_section[-1]
+ # Check: signatures_{offset,size} must match the last (fake) operation.
+ if not (fake_sig_op.type == common.OpType.REPLACE and
+ self.sigs_offset == fake_sig_op.data_offset and
+ self.sigs_size == fake_sig_op.data_length):
+ raise error.PayloadError('Signatures_{offset,size} (%d+%d) does not'
+ ' match last operation (%d+%d).' %
+ (self.sigs_offset, self.sigs_size,
+ fake_sig_op.data_offset,
+ fake_sig_op.data_length))
# Compute the checksum of all data up to signature blob.
# TODO(garnold) we're re-reading the whole data section into a string
@@ -1314,9 +1318,9 @@
try:
# Check metadata_size (if provided).
- if metadata_size and self.payload.data_offset != metadata_size:
+ if metadata_size and self.payload.metadata_size != metadata_size:
raise error.PayloadError('Invalid payload metadata size in payload(%d) '
- 'vs given(%d)' % (self.payload.data_offset,
+ 'vs given(%d)' % (self.payload.metadata_size,
metadata_size))
# Check metadata signature (if provided).
@@ -1343,7 +1347,7 @@
# Part 3: Examine partition operations.
install_operations = []
- if self.major_version == 1:
+ if self.major_version == common.CHROMEOS_MAJOR_PAYLOAD_VERSION:
# partitions field should not ever exist in major version 1 payloads
self._CheckRepeatedElemNotPresent(manifest, 'partitions', 'manifest')
@@ -1386,10 +1390,17 @@
operations, report, '%s_install_operations' % part,
self.old_fs_sizes[part], self.new_fs_sizes[part],
old_fs_usable_size, new_fs_usable_size, total_blob_size,
- self.major_version == 1 and part == common.KERNEL)
+ (self.major_version == common.CHROMEOS_MAJOR_PAYLOAD_VERSION
+ and part == common.KERNEL))
# Check: Operations data reach the end of the payload file.
used_payload_size = self.payload.data_offset + total_blob_size
+ # Major versions 2 and higher have a signature at the end, so it should be
+ # considered in the total size of the image.
+ if (self.major_version >= common.BRILLO_MAJOR_PAYLOAD_VERSION and
+ self.sigs_size):
+ used_payload_size += self.sigs_size
+
if used_payload_size != payload_file_size:
raise error.PayloadError(
'Used payload size (%d) different from actual file size (%d).' %
diff --git a/scripts/update_payload/checker_unittest.py b/scripts/update_payload/checker_unittest.py
index 98bf612..7e52233 100755
--- a/scripts/update_payload/checker_unittest.py
+++ b/scripts/update_payload/checker_unittest.py
@@ -892,7 +892,7 @@
total_src_blocks = 16
# TODO(tbrindus): add major version 2 tests.
- payload_checker.major_version = 1
+ payload_checker.major_version = common.CHROMEOS_MAJOR_PAYLOAD_VERSION
if op_type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
payload_checker.minor_version = 0
elif op_type in (common.OpType.MOVE, common.OpType.BSDIFF):
diff --git a/system_state.h b/system_state.h
index 1b0ad08..f46cbcf 100644
--- a/system_state.h
+++ b/system_state.h
@@ -37,6 +37,7 @@
class BootControlInterface;
class ClockInterface;
class ConnectionManagerInterface;
+class DlcServiceInterface;
class HardwareInterface;
class MetricsReporterInterface;
class OmahaRequestParams;
@@ -109,6 +110,9 @@
// restarted. Important for tracking whether you are running instance of the
// update engine on first boot or due to a crash/restart.
virtual bool system_rebooted() = 0;
+
+ // Returns a pointer to the DlcServiceInterface singleton.
+ virtual DlcServiceInterface* dlcservice() = 0;
};
} // namespace chromeos_update_engine
diff --git a/update_attempter.cc b/update_attempter.cc
index 63d8a61..5efd257 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -30,6 +30,7 @@
#include <base/rand_util.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
#include <brillo/data_encoding.h>
#include <brillo/errors/error_codes.h>
#include <brillo/message_loops/message_loop.h>
@@ -41,6 +42,7 @@
#include "update_engine/common/boot_control_interface.h"
#include "update_engine/common/clock_interface.h"
#include "update_engine/common/constants.h"
+#include "update_engine/common/dlcservice_interface.h"
#include "update_engine/common/hardware_interface.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/common/prefs_interface.h"
@@ -123,7 +125,8 @@
CertificateChecker* cert_checker)
: processor_(new ActionProcessor()),
system_state_(system_state),
- cert_checker_(cert_checker) {}
+ cert_checker_(cert_checker),
+ is_install_(false) {}
UpdateAttempter::~UpdateAttempter() {
// CertificateChecker might not be initialized in unittests.
@@ -152,9 +155,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();
@@ -165,6 +168,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,
@@ -411,6 +415,9 @@
// target channel.
omaha_request_params_->UpdateDownloadChannel();
}
+ // Set the DLC module ID list.
+ omaha_request_params_->set_dlc_module_ids(dlc_module_ids_);
+ omaha_request_params_->set_is_install(is_install_);
LOG(INFO) << "target_version_prefix = "
<< omaha_request_params_->target_version_prefix()
@@ -692,6 +699,7 @@
}
bool UpdateAttempter::Rollback(bool powerwash) {
+ is_install_ = false;
if (!CanRollback()) {
return false;
}
@@ -786,6 +794,8 @@
bool UpdateAttempter::CheckForUpdate(const string& app_version,
const string& omaha_url,
UpdateAttemptFlags flags) {
+ dlc_module_ids_.clear();
+ is_install_ = false;
bool interactive = !(flags & UpdateAttemptFlags::kFlagNonInteractive);
if (interactive && status_ != UpdateStatus::IDLE) {
@@ -827,6 +837,9 @@
}
if (forced_update_pending_callback_.get()) {
+ if (!system_state_->dlcservice()->GetInstalled(&dlc_module_ids_)) {
+ dlc_module_ids_.clear();
+ }
// Make sure that a scheduling request is made prior to calling the forced
// update pending callback.
ScheduleUpdates();
@@ -836,6 +849,37 @@
return true;
}
+bool UpdateAttempter::CheckForInstall(const vector<string>& dlc_module_ids,
+ const string& omaha_url) {
+ dlc_module_ids_ = dlc_module_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) {
@@ -975,7 +1019,12 @@
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);
prefs_->SetString(kPrefsPreviousVersion,
omaha_request_params_->app_version());
@@ -997,6 +1046,13 @@
system_state_->payload_state()->SetStagingWaitPeriod(TimeDelta());
prefs_->Delete(kPrefsUpdateFirstSeenAt);
+ if (is_install_) {
+ LOG(INFO) << "DLC successfully installed, no reboot needed.";
+ SetStatusAndNotify(UpdateStatus::IDLE);
+ ScheduleUpdates();
+ return;
+ }
+
SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
ScheduleUpdates();
LOG(INFO) << "Update successfully applied, waiting to reboot.";
@@ -1604,4 +1660,26 @@
return false;
}
+void UpdateAttempter::ReportTimeToUpdateAppliedMetric() {
+ const policy::DevicePolicy* device_policy = system_state_->device_policy();
+ if (device_policy && device_policy->IsEnterpriseEnrolled()) {
+ vector<policy::DevicePolicy::WeeklyTimeInterval> parsed_intervals;
+ bool has_time_restrictions =
+ device_policy->GetDisallowedTimeIntervals(&parsed_intervals);
+
+ int64_t update_first_seen_at_int;
+ if (system_state_->prefs()->Exists(kPrefsUpdateFirstSeenAt)) {
+ if (system_state_->prefs()->GetInt64(kPrefsUpdateFirstSeenAt,
+ &update_first_seen_at_int)) {
+ TimeDelta update_delay =
+ system_state_->clock()->GetWallclockTime() -
+ Time::FromInternalValue(update_first_seen_at_int);
+ system_state_->metrics_reporter()
+ ->ReportEnterpriseUpdateSeenToDownloadDays(has_time_restrictions,
+ update_delay.InDays());
+ }
+ }
+ }
+}
+
} // namespace chromeos_update_engine
diff --git a/update_attempter.h b/update_attempter.h
index d0beff6..af62ba6 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -69,7 +69,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
@@ -135,6 +136,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_module_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
@@ -241,14 +246,17 @@
FRIEND_TEST(UpdateAttempterTest, BootTimeInUpdateMarkerFile);
FRIEND_TEST(UpdateAttempterTest, BroadcastCompleteDownloadTest);
FRIEND_TEST(UpdateAttempterTest, ChangeToDownloadingOnReceivedBytesTest);
+ FRIEND_TEST(UpdateAttempterTest, CheckForUpdateAUDlcTest);
FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventTest);
FRIEND_TEST(UpdateAttempterTest, CreatePendingErrorEventResumedTest);
FRIEND_TEST(UpdateAttempterTest, DisableDeltaUpdateIfNeededTest);
FRIEND_TEST(UpdateAttempterTest, DownloadProgressAccumulationTest);
+ FRIEND_TEST(UpdateAttempterTest, InstallSetsStatusIdle);
FRIEND_TEST(UpdateAttempterTest, MarkDeltaUpdateFailureTest);
FRIEND_TEST(UpdateAttempterTest, PingOmahaTest);
FRIEND_TEST(UpdateAttempterTest, ReportDailyMetrics);
FRIEND_TEST(UpdateAttempterTest, RollbackNotAllowed);
+ FRIEND_TEST(UpdateAttempterTest, RollbackAfterInstall);
FRIEND_TEST(UpdateAttempterTest, RollbackAllowed);
FRIEND_TEST(UpdateAttempterTest, RollbackAllowedSetAndReset);
FRIEND_TEST(UpdateAttempterTest, RollbackMetricsNotRollbackFailure);
@@ -260,6 +268,7 @@
FRIEND_TEST(UpdateAttempterTest, SetRollbackHappenedNotRollback);
FRIEND_TEST(UpdateAttempterTest, SetRollbackHappenedRollback);
FRIEND_TEST(UpdateAttempterTest, TargetVersionPrefixSetAndReset);
+ FRIEND_TEST(UpdateAttempterTest, UpdateAfterInstall);
FRIEND_TEST(UpdateAttempterTest, UpdateAttemptFlagsCachedAtUpdateStart);
FRIEND_TEST(UpdateAttempterTest, UpdateDeferredByPolicyTest);
FRIEND_TEST(UpdateAttempterTest, UpdateIsNotRunningWhenUpdateAvailable);
@@ -394,11 +403,22 @@
void CalculateStagingParams(bool interactive);
+ // Reports a metric that tracks the time from when the update was first seen
+ // to the time when the update was finally downloaded and applied. This metric
+ // will only be reported for enterprise enrolled devices.
+ void ReportTimeToUpdateAppliedMetric();
+
// Last status notification timestamp used for throttling. Use monotonic
// TimeTicks to ensure that notifications are sent even if the system clock is
// set back in the middle of an update.
base::TimeTicks last_notify_time_;
+ // Our two proxy resolvers
+ DirectProxyResolver direct_proxy_resolver_;
+#if USE_CHROME_NETWORK_PROXY
+ ChromeBrowserProxyResolver chrome_proxy_resolver_;
+#endif // USE_CHROME_NETWORK_PROXY
+
std::unique_ptr<ActionProcessor> processor_;
// External state of the system outside the update_engine process
@@ -458,12 +478,6 @@
// If true, this update cycle we are obeying proxies
bool obeying_proxies_ = true;
- // Our two proxy resolvers
- DirectProxyResolver direct_proxy_resolver_;
-#if USE_CHROME_NETWORK_PROXY
- ChromeBrowserProxyResolver chrome_proxy_resolver_;
-#endif // USE_CHROME_NETWORK_PROXY
-
// Used for fetching information about the device policy.
std::unique_ptr<policy::PolicyProvider> policy_provider_;
@@ -494,6 +508,12 @@
std::string forced_app_version_;
std::string forced_omaha_url_;
+ // A list of DLC module IDs.
+ std::vector<std::string> dlc_module_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_android.h b/update_attempter_android.h
index 0438c16..e4b40de 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -168,6 +168,9 @@
// set back in the middle of an update.
base::TimeTicks last_notify_time_;
+ // Only direct proxy supported.
+ DirectProxyResolver proxy_resolver_;
+
// The processor for running Actions.
std::unique_ptr<ActionProcessor> processor_;
@@ -181,9 +184,6 @@
// The offset in the payload file where the CrAU part starts.
int64_t base_offset_{0};
- // Only direct proxy supported.
- DirectProxyResolver proxy_resolver_;
-
// Helper class to select the network to use during the update.
std::unique_ptr<NetworkSelectorInterface> network_selector_;
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index fb9f7bc..3209f15 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -30,6 +30,7 @@
#include <policy/mock_device_policy.h>
#include <policy/mock_libpolicy.h>
+#include "update_engine/common/dlcservice_interface.h"
#include "update_engine/common/fake_clock.h"
#include "update_engine/common/fake_prefs.h"
#include "update_engine/common/mock_action.h"
@@ -58,6 +59,7 @@
using policy::DevicePolicy;
using std::string;
using std::unique_ptr;
+using std::vector;
using testing::_;
using testing::DoAll;
using testing::Field;
@@ -77,6 +79,15 @@
namespace chromeos_update_engine {
+namespace {
+
+class MockDlcService : public DlcServiceInterface {
+ public:
+ MOCK_METHOD1(GetInstalled, bool(vector<string>*));
+};
+
+} // namespace
+
const char kRollbackVersion[] = "10575.39.2";
// Test a subclass rather than the main class directly so that we can mock out
@@ -89,13 +100,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; }
@@ -119,10 +131,13 @@
// Override system state members.
fake_system_state_.set_connection_manager(&mock_connection_manager);
fake_system_state_.set_update_attempter(&attempter_);
+ fake_system_state_.set_dlcservice(&mock_dlcservice_);
loop_.SetAsCurrent();
certificate_checker_.Init();
+ attempter_.set_forced_update_pending_callback(
+ new base::Callback<void(bool, bool)>(base::Bind([](bool, bool) {})));
// Finish initializing the attempter.
attempter_.Init();
}
@@ -197,6 +212,7 @@
UpdateAttempterUnderTest attempter_{&fake_system_state_};
OpenSSLWrapper openssl_wrapper_;
CertificateChecker certificate_checker_;
+ MockDlcService mock_dlcservice_;
NiceMock<MockActionProcessor>* processor_;
NiceMock<MockPrefs>* prefs_; // Shortcut to fake_system_state_->mock_prefs().
@@ -1158,6 +1174,21 @@
EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
}
+TEST_F(UpdateAttempterTest, CheckForUpdateAUDlcTest) {
+ fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
+ fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
+
+ const string dlc_module_id = "a_dlc_module_id";
+ vector<string> dlc_module_ids = {dlc_module_id};
+ ON_CALL(mock_dlcservice_, GetInstalled(testing::_))
+ .WillByDefault(DoAll(testing::SetArgPointee<0>(dlc_module_ids),
+ testing::Return(true)));
+
+ attempter_.CheckForUpdate("", "autest", UpdateAttemptFlags::kNone);
+ EXPECT_EQ(attempter_.dlc_module_ids_.size(), 1);
+ EXPECT_EQ(attempter_.dlc_module_ids_[0], dlc_module_id);
+}
+
TEST_F(UpdateAttempterTest, CheckForUpdateAUTest) {
fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
fake_system_state_.fake_hardware()->SetAreDevFeaturesEnabled(false);
@@ -1172,6 +1203,42 @@
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, InstallSetsStatusIdle) {
+ attempter_.CheckForInstall({}, "http://foo.bar");
+ attempter_.status_ = UpdateStatus::DOWNLOADING;
+ EXPECT_TRUE(attempter_.is_install_);
+ attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+ UpdateEngineStatus status;
+ attempter_.GetStatus(&status);
+ // Should set status to idle after an install operation.
+ EXPECT_EQ(UpdateStatus::IDLE, status.status);
+}
+
+TEST_F(UpdateAttempterTest, RollbackAfterInstall) {
+ attempter_.is_install_ = true;
+ attempter_.Rollback(false);
+ EXPECT_FALSE(attempter_.is_install_);
+}
+
+TEST_F(UpdateAttempterTest, UpdateAfterInstall) {
+ attempter_.is_install_ = true;
+ attempter_.CheckForUpdate("", "", UpdateAttemptFlags::kNone);
+ EXPECT_FALSE(attempter_.is_install_);
+}
+
TEST_F(UpdateAttempterTest, TargetVersionPrefixSetAndReset) {
attempter_.CalculateUpdateParams("", "", "", "1234", false, false, false);
EXPECT_EQ("1234",
@@ -1421,4 +1488,84 @@
attempter_.ProcessingDone(nullptr, ErrorCode::kRollbackNotPossible);
}
+TEST_F(UpdateAttempterTest, TimeToUpdateAppliedMetricFailure) {
+ EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+ ReportEnterpriseUpdateSeenToDownloadDays(_, _))
+ .Times(0);
+ attempter_.ProcessingDone(nullptr, ErrorCode::kOmahaUpdateDeferredPerPolicy);
+}
+
+TEST_F(UpdateAttempterTest, TimeToUpdateAppliedOnNonEnterprise) {
+ auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+ fake_system_state_.set_device_policy(device_policy.get());
+ // Make device policy return that this is not enterprise enrolled
+ EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(false));
+
+ // Ensure that the metric is not recorded.
+ EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+ ReportEnterpriseUpdateSeenToDownloadDays(_, _))
+ .Times(0);
+ attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+TEST_F(UpdateAttempterTest,
+ TimeToUpdateAppliedWithTimeRestrictionMetricSuccess) {
+ constexpr int kDaysToUpdate = 15;
+ auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+ fake_system_state_.set_device_policy(device_policy.get());
+ // Make device policy return that this is enterprise enrolled
+ EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(true));
+ // Pretend that there's a time restriction policy in place
+ EXPECT_CALL(*device_policy, GetDisallowedTimeIntervals(_))
+ .WillOnce(Return(true));
+
+ FakePrefs fake_prefs;
+ Time update_first_seen_at = Time::Now();
+ fake_prefs.SetInt64(kPrefsUpdateFirstSeenAt,
+ update_first_seen_at.ToInternalValue());
+
+ FakeClock fake_clock;
+ Time update_finished_at =
+ update_first_seen_at + TimeDelta::FromDays(kDaysToUpdate);
+ fake_clock.SetWallclockTime(update_finished_at);
+
+ fake_system_state_.set_clock(&fake_clock);
+ fake_system_state_.set_prefs(&fake_prefs);
+
+ EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+ ReportEnterpriseUpdateSeenToDownloadDays(true, kDaysToUpdate))
+ .Times(1);
+ attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+TEST_F(UpdateAttempterTest,
+ TimeToUpdateAppliedWithoutTimeRestrictionMetricSuccess) {
+ constexpr int kDaysToUpdate = 15;
+ auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+ fake_system_state_.set_device_policy(device_policy.get());
+ // Make device policy return that this is enterprise enrolled
+ EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(true));
+ // Pretend that there's no time restriction policy in place
+ EXPECT_CALL(*device_policy, GetDisallowedTimeIntervals(_))
+ .WillOnce(Return(false));
+
+ FakePrefs fake_prefs;
+ Time update_first_seen_at = Time::Now();
+ fake_prefs.SetInt64(kPrefsUpdateFirstSeenAt,
+ update_first_seen_at.ToInternalValue());
+
+ FakeClock fake_clock;
+ Time update_finished_at =
+ update_first_seen_at + TimeDelta::FromDays(kDaysToUpdate);
+ fake_clock.SetWallclockTime(update_finished_at);
+
+ fake_system_state_.set_clock(&fake_clock);
+ fake_system_state_.set_prefs(&fake_prefs);
+
+ EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+ ReportEnterpriseUpdateSeenToDownloadDays(false, kDaysToUpdate))
+ .Times(1);
+ attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
} // namespace chromeos_update_engine
diff --git a/update_engine.gyp b/update_engine.gyp
index 358f64a..2d81bed 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -13,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+
+# TODO: Rename these files to pass this check.
+# gyplint: disable=GypLintSourceFileNames
{
'variables': {
'USE_chrome_network_proxy': '1',
@@ -23,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
@@ -73,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',
@@ -131,6 +128,7 @@
'exported_deps': [
'libcrypto',
'xz-embedded',
+ 'libbspatch',
'libpuffpatch',
],
'deps': ['<@(exported_deps)'],
@@ -149,7 +147,6 @@
],
},
'libraries': [
- '-lbspatch',
'-lbz2',
'-lrt',
],
@@ -228,6 +225,13 @@
'libupdate_engine-client',
'vboot_host',
],
+ 'conditions':[
+ ['USE_dlc == 1', {
+ 'exported_deps' : [
+ 'libdlcservice-client',
+ ],
+ }],
+ ],
'deps': ['<@(exported_deps)'],
},
'all_dependent_settings': {
@@ -312,6 +316,16 @@
'update_engine-dbus-kiosk-app-client',
],
}],
+ ['USE_dlc == 1', {
+ 'sources': [
+ 'dlcservice_chromeos.cc',
+ ],
+ }],
+ ['USE_dlc == 0', {
+ 'sources': [
+ 'common/dlcservice_stub.cc',
+ ],
+ }],
],
},
# update_engine daemon.
@@ -369,6 +383,7 @@
'variables': {
'exported_deps': [
'ext2fs',
+ 'libbsdiff',
'libpuffdiff',
'liblzma',
],
@@ -387,9 +402,6 @@
'<@(exported_deps)',
],
},
- 'libraries': [
- '-lbsdiff',
- ],
},
'sources': [
'payload_generator/ab_generator.cc',
@@ -438,6 +450,32 @@
'payload_generator/generate_delta_main.cc',
],
},
+ {
+ 'target_name': 'update_engine_test_libs',
+ 'type': 'static_library',
+ 'variables': {
+ 'deps': [
+ 'libshill-client-test',
+ ],
+ },
+ 'dependencies': [
+ 'libupdate_engine',
+ ],
+ 'includes': [
+ '../../../platform2/common-mk/common_test.gypi',
+ ],
+ 'sources': [
+ 'common/fake_prefs.cc',
+ 'common/file_fetcher.cc', # Only required for tests.
+ 'common/mock_http_fetcher.cc',
+ 'common/test_utils.cc',
+ 'fake_shill_proxy.cc',
+ 'fake_system_state.cc',
+ 'payload_consumer/fake_file_descriptor.cc',
+ 'payload_generator/fake_filesystem.cc',
+ 'update_manager/umtest_utils.cc',
+ ],
+ },
],
'conditions': [
['USE_test == 1', {
@@ -502,8 +540,8 @@
'dependencies': [
'libupdate_engine',
'libpayload_generator',
+ 'update_engine_test_libs',
],
- 'includes': ['../../../platform2/common-mk/common_test.gypi'],
'sources': [
'boot_control_chromeos_unittest.cc',
'certificate_checker_unittest.cc',
@@ -511,22 +549,16 @@
'common/action_processor_unittest.cc',
'common/action_unittest.cc',
'common/cpu_limiter_unittest.cc',
- 'common/fake_prefs.cc',
- 'common/file_fetcher.cc', # Only required for tests.
'common/hash_calculator_unittest.cc',
'common/http_fetcher_unittest.cc',
'common/hwid_override_unittest.cc',
- 'common/mock_http_fetcher.cc',
'common/prefs_unittest.cc',
'common/proxy_resolver_unittest.cc',
'common/subprocess_unittest.cc',
'common/terminator_unittest.cc',
- 'common/test_utils.cc',
'common/utils_unittest.cc',
'common_service_unittest.cc',
'connection_manager_unittest.cc',
- 'fake_shill_proxy.cc',
- 'fake_system_state.cc',
'hardware_chromeos_unittest.cc',
'image_properties_chromeos_unittest.cc',
'metrics_reporter_omaha_unittest.cc',
@@ -543,7 +575,6 @@
'payload_consumer/download_action_unittest.cc',
'payload_consumer/extent_reader_unittest.cc',
'payload_consumer/extent_writer_unittest.cc',
- 'payload_consumer/fake_file_descriptor.cc',
'payload_consumer/file_descriptor_utils_unittest.cc',
'payload_consumer/file_writer_unittest.cc',
'payload_consumer/filesystem_verifier_action_unittest.cc',
@@ -559,7 +590,6 @@
'payload_generator/ext2_filesystem_unittest.cc',
'payload_generator/extent_ranges_unittest.cc',
'payload_generator/extent_utils_unittest.cc',
- 'payload_generator/fake_filesystem.cc',
'payload_generator/full_update_generator_unittest.cc',
'payload_generator/graph_utils_unittest.cc',
'payload_generator/inplace_generator_unittest.cc',
@@ -587,7 +617,6 @@
'update_manager/real_time_provider_unittest.cc',
'update_manager/real_updater_provider_unittest.cc',
'update_manager/staging_utils_unittest.cc',
- 'update_manager/umtest_utils.cc',
'update_manager/update_manager_unittest.cc',
'update_manager/update_time_restrictions_policy_impl_unittest.cc',
'update_manager/variable_unittest.cc',
@@ -596,5 +625,30 @@
},
],
}],
+ # Fuzzer target.
+ ['USE_fuzzer == 1', {
+ 'targets': [
+ {
+ 'target_name': 'update_engine_omaha_request_action_fuzzer',
+ 'type': 'executable',
+ 'variables': {
+ 'deps': [
+ 'libbrillo-test-<(libbase_ver)',
+ 'libchrome-test-<(libbase_ver)',
+ ],
+ },
+ 'includes': [
+ '../../../platform2/common-mk/common_fuzzer.gypi',
+ ],
+ 'dependencies': [
+ 'libupdate_engine',
+ 'update_engine_test_libs',
+ ],
+ 'sources': [
+ 'omaha_request_action_fuzzer.cc',
+ ],
+ },
+ ],
+ }],
],
}
diff --git a/update_engine_client.cc b/update_engine_client.cc
index b7096c5..f36949b 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_module_ids, "", "colon-separated list of DLC IDs.");
// Boilerplate init commands.
base::CommandLine::Init(argc_, argv_);
@@ -477,6 +480,30 @@
}
}
+ if (FLAGS_install) {
+ // Parse DLC module IDs.
+ vector<string> dlc_module_ids;
+ if (!FLAGS_dlc_module_ids.empty()) {
+ dlc_module_ids = base::SplitString(FLAGS_dlc_module_ids,
+ ":",
+ base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_ALL);
+ }
+ if (dlc_module_ids.empty()) {
+ LOG(ERROR) << "dlc_module_ids is empty:" << FLAGS_dlc_module_ids;
+ return 1;
+ }
+ if (!client_->AttemptInstall(FLAGS_omaha_url, dlc_module_ids)) {
+ LOG(ERROR) << "AttemptInstall failed.";
+ return 1;
+ }
+ return 0;
+ } else if (!FLAGS_dlc_module_ids.empty()) {
+ LOG(ERROR) << "dlc_module_ids is not empty while install is not set:"
+ << FLAGS_dlc_module_ids;
+ return 1;
+ }
+
// Initiate an update check, if necessary.
if (do_update_request) {
LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored.";
diff --git a/update_manager/boxed_value.cc b/update_manager/boxed_value.cc
index 8ec3375..ffbd00b 100644
--- a/update_manager/boxed_value.cc
+++ b/update_manager/boxed_value.cc
@@ -144,8 +144,12 @@
return "Unspecified";
case RollbackToTargetVersion::kDisabled:
return "Disabled";
- case RollbackToTargetVersion::kRollbackWithFullPowerwash:
- return "Rollback with full powerwash";
+ case RollbackToTargetVersion::kRollbackAndPowerwash:
+ return "Rollback and powerwash";
+ case RollbackToTargetVersion::kRollbackAndRestoreIfPossible:
+ return "Rollback and restore if possible";
+ case RollbackToTargetVersion::kRollbackOnlyIfRestorePossible:
+ return "Rollback only if restore is possible";
case RollbackToTargetVersion::kMaxValue:
NOTREACHED();
return "Max value";
diff --git a/update_manager/boxed_value_unittest.cc b/update_manager/boxed_value_unittest.cc
index 3fa0f1a..212db36 100644
--- a/update_manager/boxed_value_unittest.cc
+++ b/update_manager/boxed_value_unittest.cc
@@ -161,6 +161,9 @@
}
TEST(UmBoxedValueTest, ConnectionTypeToString) {
+ EXPECT_EQ(
+ "Disconnected",
+ BoxedValue(new ConnectionType(ConnectionType::kDisconnected)).ToString());
EXPECT_EQ("ethernet",
BoxedValue(new ConnectionType(ConnectionType::kEthernet))
.ToString());
@@ -203,10 +206,20 @@
BoxedValue(
new RollbackToTargetVersion(RollbackToTargetVersion::kDisabled))
.ToString());
- EXPECT_EQ("Rollback with full powerwash",
+ EXPECT_EQ("Rollback and powerwash",
BoxedValue(new RollbackToTargetVersion(
- RollbackToTargetVersion::kRollbackWithFullPowerwash))
+ RollbackToTargetVersion::kRollbackAndPowerwash))
.ToString());
+ EXPECT_EQ(
+ "Rollback and restore if possible",
+ BoxedValue(new RollbackToTargetVersion(
+ RollbackToTargetVersion::kRollbackAndRestoreIfPossible))
+ .ToString());
+ EXPECT_EQ(
+ "Rollback only if restore is possible",
+ BoxedValue(new RollbackToTargetVersion(
+ RollbackToTargetVersion::kRollbackOnlyIfRestorePossible))
+ .ToString());
}
TEST(UmBoxedValueTest, SetConnectionTypeToString) {
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
index 96f3d79..15bb09f 100644
--- a/update_manager/chromeos_policy_unittest.cc
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -273,9 +273,21 @@
EXPECT_FALSE(result.interactive);
}
-TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackAllowed) {
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackAndPowerwash) {
EXPECT_TRUE(TestRollbackAllowed(
- true, RollbackToTargetVersion::kRollbackWithFullPowerwash));
+ true, RollbackToTargetVersion::kRollbackAndPowerwash));
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackAndRestoreIfPossible) {
+ // We're doing rollback even if we don't support data save and restore.
+ EXPECT_TRUE(TestRollbackAllowed(
+ true, RollbackToTargetVersion::kRollbackAndRestoreIfPossible));
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackOnlyIfRestorePossible) {
+ // We're not allowed to do rollback until we support data save and restore.
+ EXPECT_FALSE(TestRollbackAllowed(
+ true, RollbackToTargetVersion::kRollbackOnlyIfRestorePossible));
}
TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackDisabled) {
@@ -296,7 +308,7 @@
SetKioskAppControlsChromeOsVersion();
EXPECT_TRUE(TestRollbackAllowed(
- true, RollbackToTargetVersion::kRollbackWithFullPowerwash));
+ true, RollbackToTargetVersion::kRollbackAndPowerwash));
}
TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedKioskRollbackDisabled) {
diff --git a/update_manager/enterprise_device_policy_impl.cc b/update_manager/enterprise_device_policy_impl.cc
index 6f14b1f..a3430ef 100644
--- a/update_manager/enterprise_device_policy_impl.cc
+++ b/update_manager/enterprise_device_policy_impl.cc
@@ -92,10 +92,22 @@
LOG(INFO) << "Policy disables rollbacks.";
result->rollback_allowed = false;
break;
- case RollbackToTargetVersion::kRollbackWithFullPowerwash:
- LOG(INFO) << "Policy allows rollbacks.";
+ case RollbackToTargetVersion::kRollbackAndPowerwash:
+ LOG(INFO) << "Policy allows rollbacks with powerwash.";
result->rollback_allowed = true;
break;
+ case RollbackToTargetVersion::kRollbackAndRestoreIfPossible:
+ LOG(INFO)
+ << "Policy allows rollbacks, also tries to restore if possible.";
+ // We don't support restore yet, but policy still allows rollback.
+ result->rollback_allowed = true;
+ break;
+ case RollbackToTargetVersion::kRollbackOnlyIfRestorePossible:
+ LOG(INFO) << "Policy only allows rollbacks if restore is possible.";
+ // We don't support restore yet, policy doesn't allow rollback in this
+ // case.
+ result->rollback_allowed = false;
+ break;
case RollbackToTargetVersion::kMaxValue:
NOTREACHED();
// Don't add a default case to let the compiler warn about newly
diff --git a/update_manager/real_device_policy_provider_unittest.cc b/update_manager/real_device_policy_provider_unittest.cc
index 32e273d..e9c7b25 100644
--- a/update_manager/real_device_policy_provider_unittest.cc
+++ b/update_manager/real_device_policy_provider_unittest.cc
@@ -245,7 +245,7 @@
loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(
- RollbackToTargetVersion::kRollbackWithFullPowerwash,
+ RollbackToTargetVersion::kRollbackAndPowerwash,
provider_->var_rollback_to_target_version());
}
diff --git a/update_manager/rollback_prefs.h b/update_manager/rollback_prefs.h
index 1783eb0..11d09d6 100644
--- a/update_manager/rollback_prefs.h
+++ b/update_manager/rollback_prefs.h
@@ -29,9 +29,11 @@
enum class RollbackToTargetVersion {
kUnspecified = 0,
kDisabled = 1,
- kRollbackWithFullPowerwash = 2,
+ kRollbackAndPowerwash = 2,
+ kRollbackAndRestoreIfPossible = 3,
+ kRollbackOnlyIfRestorePossible = 4,
// This value must be the last entry.
- kMaxValue = 3
+ kMaxValue = 5
};
} // namespace chromeos_update_manager