Read and write channel from misc.
Use the new update_channel field in bootloader_message_ab in misc partition
to store the target channel.
Bug: 72332119
Test: update_engine_unittests
Change-Id: I11c3d1705ff8724e66b595f83409cb2678708aff
diff --git a/image_properties_android.cc b/image_properties_android.cc
index 1ec425d..6e743f9 100644
--- a/image_properties_android.cc
+++ b/image_properties_android.cc
@@ -16,10 +16,13 @@
#include "update_engine/image_properties.h"
+#include <fcntl.h>
+
#include <string>
#include <android-base/properties.h>
#include <base/logging.h>
+#include <bootloader.h>
#include <brillo/osrelease_reader.h>
#include <brillo/strings/string_utils.h>
@@ -29,6 +32,7 @@
#include "update_engine/common/prefs_interface.h"
#include "update_engine/common/utils.h"
#include "update_engine/system_state.h"
+#include "update_engine/utils_android.h"
using android::base::GetProperty;
using std::string;
@@ -47,8 +51,7 @@
// components in OEM partition.
const char kProductComponentsPath[] = "/oem/os-release.d/product_components";
-// Prefs used to store the target channel and powerwash settings.
-const char kPrefsImgPropChannelName[] = "img-prop-channel-name";
+// Prefs used to store the powerwash settings.
const char kPrefsImgPropPowerwashAllowed[] = "img-prop-powerwash-allowed";
// System properties that identifies the "board".
@@ -56,6 +59,9 @@
const char kPropBuildFingerprint[] = "ro.build.fingerprint";
const char kPropBuildType[] = "ro.build.type";
+// Default channel from factory.prop
+const char kPropDefaultChannel[] = "ro.update.default_channel";
+
// A prefix added to the path, used for testing.
const char* root_prefix = nullptr;
@@ -70,6 +76,81 @@
return default_value;
}
+// Open misc partition for read or write and output the fd in |out_fd|.
+bool OpenMisc(bool write, int* out_fd) {
+ base::FilePath misc_device;
+ int flags = write ? O_WRONLY | O_SYNC : O_RDONLY;
+ if (root_prefix) {
+ // Use a file for unittest and create one if doesn't exist.
+ misc_device = base::FilePath(root_prefix).Append("misc");
+ if (write)
+ flags |= O_CREAT;
+ } else if (!utils::DeviceForMountPoint("/misc", &misc_device)) {
+ return false;
+ }
+
+ int fd = HANDLE_EINTR(open(misc_device.value().c_str(), flags, 0600));
+ if (fd < 0) {
+ PLOG(ERROR) << "Opening misc failed";
+ return false;
+ }
+ *out_fd = fd;
+ return true;
+}
+
+// The offset and size of the channel field in misc partition.
+constexpr size_t kChannelOffset =
+ BOOTLOADER_MESSAGE_OFFSET_IN_MISC +
+ offsetof(bootloader_message_ab, update_channel);
+constexpr size_t kChannelSize = sizeof(bootloader_message_ab::update_channel);
+
+// Read channel from misc partition to |out_channel|, return false if unable to
+// read misc or no channel is set in misc.
+bool ReadChannelFromMisc(string* out_channel) {
+ int fd;
+ TEST_AND_RETURN_FALSE(OpenMisc(false, &fd));
+ ScopedFdCloser fd_closer(&fd);
+ char channel[kChannelSize] = {0};
+ ssize_t bytes_read = 0;
+ if (!utils::PReadAll(
+ fd, channel, kChannelSize - 1, kChannelOffset, &bytes_read) ||
+ bytes_read != kChannelSize - 1) {
+ PLOG(ERROR) << "Reading update channel from misc failed";
+ return false;
+ }
+ if (channel[0] == '\0') {
+ LOG(INFO) << "No channel set in misc.";
+ return false;
+ }
+ out_channel->assign(channel);
+ return true;
+}
+
+// Write |in_channel| to misc partition, return false if failed to write.
+bool WriteChannelToMisc(const string& in_channel) {
+ int fd;
+ TEST_AND_RETURN_FALSE(OpenMisc(true, &fd));
+ ScopedFdCloser fd_closer(&fd);
+ if (in_channel.size() >= kChannelSize) {
+ LOG(ERROR) << "Channel name is too long: " << in_channel
+ << ", the maximum length is " << kChannelSize - 1;
+ return false;
+ }
+ char channel[kChannelSize] = {0};
+ memcpy(channel, in_channel.data(), in_channel.size());
+ if (!utils::PWriteAll(fd, channel, kChannelSize, kChannelOffset)) {
+ PLOG(ERROR) << "Writing update channel to misc failed";
+ return false;
+ }
+ return true;
+}
+
+string GetTargetChannel() {
+ string channel;
+ if (!ReadChannelFromMisc(&channel))
+ channel = GetProperty(kPropDefaultChannel, "stable-channel");
+ return channel;
+}
} // namespace
namespace test {
@@ -109,17 +190,16 @@
result.build_fingerprint = GetProperty(kPropBuildFingerprint, "none");
result.build_type = GetProperty(kPropBuildType, "");
- // Brillo images don't have a channel assigned. We stored the name of the
- // channel where we got the image from in prefs at the time of the update, so
- // we use that as the current channel if available. During provisioning, there
- // is no value assigned, so we default to the "stable-channel".
+ // Android doesn't have channel information in system image, we try to read
+ // the channel of current slot from prefs and then fallback to use the
+ // persisted target channel as current channel.
string current_channel_key =
kPrefsChannelOnSlotPrefix +
std::to_string(system_state->boot_control()->GetCurrentSlot());
string current_channel;
if (!system_state->prefs()->Exists(current_channel_key) ||
!system_state->prefs()->GetString(current_channel_key, ¤t_channel))
- current_channel = "stable-channel";
+ current_channel = GetTargetChannel();
result.current_channel = current_channel;
// Brillo only supports the official omaha URL.
@@ -130,11 +210,9 @@
MutableImageProperties LoadMutableImageProperties(SystemState* system_state) {
MutableImageProperties result;
- PrefsInterface* const prefs = system_state->prefs();
- if (!prefs->GetString(kPrefsImgPropChannelName, &result.target_channel))
- result.target_channel.clear();
- if (!prefs->GetBoolean(kPrefsImgPropPowerwashAllowed,
- &result.is_powerwash_allowed)) {
+ result.target_channel = GetTargetChannel();
+ if (!system_state->prefs()->GetBoolean(kPrefsImgPropPowerwashAllowed,
+ &result.is_powerwash_allowed)) {
result.is_powerwash_allowed = false;
}
return result;
@@ -142,11 +220,13 @@
bool StoreMutableImageProperties(SystemState* system_state,
const MutableImageProperties& properties) {
- PrefsInterface* const prefs = system_state->prefs();
- return (
- prefs->SetString(kPrefsImgPropChannelName, properties.target_channel) &&
- prefs->SetBoolean(kPrefsImgPropPowerwashAllowed,
- properties.is_powerwash_allowed));
+ bool ret = true;
+ if (!WriteChannelToMisc(properties.target_channel))
+ ret = false;
+ if (!system_state->prefs()->SetBoolean(kPrefsImgPropPowerwashAllowed,
+ properties.is_powerwash_allowed))
+ ret = false;
+ return ret;
}
void LogImageProperties() {
diff --git a/image_properties_android_unittest.cc b/image_properties_android_unittest.cc
index 7327554..607284a 100644
--- a/image_properties_android_unittest.cc
+++ b/image_properties_android_unittest.cc
@@ -23,6 +23,7 @@
#include <gtest/gtest.h>
#include "update_engine/common/constants.h"
+#include "update_engine/common/fake_prefs.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/fake_system_state.h"
@@ -45,6 +46,14 @@
ASSERT_TRUE(WriteFileString(osrelease_dir_.Append(key).value(), value));
}
+ void WriteChannel(const string& channel) {
+ string misc(2080, '\0');
+ misc += channel;
+ misc.resize(4096);
+ ASSERT_TRUE(
+ WriteFileString(tempdir_.GetPath().Append("misc").value(), misc));
+ }
+
FakeSystemState fake_system_state_;
base::ScopedTempDir tempdir_;
@@ -87,4 +96,28 @@
EXPECT_EQ("bar", props.system_id);
}
+TEST_F(ImagePropertiesTest, LoadChannelTest) {
+ WriteChannel("unittest-channel");
+ ImageProperties props = LoadImageProperties(&fake_system_state_);
+ EXPECT_EQ("unittest-channel", props.current_channel);
+}
+
+TEST_F(ImagePropertiesTest, DefaultStableChannelTest) {
+ WriteChannel("");
+ ImageProperties props = LoadImageProperties(&fake_system_state_);
+ EXPECT_EQ("stable-channel", props.current_channel);
+}
+
+TEST_F(ImagePropertiesTest, StoreLoadMutableChannelTest) {
+ FakePrefs prefs;
+ fake_system_state_.set_prefs(&prefs);
+ WriteChannel("previous-channel");
+ MutableImageProperties props;
+ props.target_channel = "new-channel";
+ EXPECT_TRUE(StoreMutableImageProperties(&fake_system_state_, props));
+ MutableImageProperties loaded_props =
+ LoadMutableImageProperties(&fake_system_state_);
+ EXPECT_EQ(props.target_channel, loaded_props.target_channel);
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index 568d11d..4e9c03f 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -388,8 +388,6 @@
params.set_current_channel("canary-channel");
// The ImageProperties in Android uses prefs to store MutableImageProperties.
#ifdef __ANDROID__
- EXPECT_CALL(*fake_system_state_.mock_prefs(), SetString(_, "stable-channel"))
- .WillOnce(Return(true));
EXPECT_CALL(*fake_system_state_.mock_prefs(), SetBoolean(_, true))
.WillOnce(Return(true));
#endif // __ANDROID__
@@ -423,8 +421,6 @@
params.set_current_channel("stable-channel");
// The ImageProperties in Android uses prefs to store MutableImageProperties.
#ifdef __ANDROID__
- EXPECT_CALL(*fake_system_state_.mock_prefs(), SetString(_, "canary-channel"))
- .WillOnce(Return(true));
EXPECT_CALL(*fake_system_state_.mock_prefs(), SetBoolean(_, false))
.WillOnce(Return(true));
#endif // __ANDROID__