update_engine: Store fingerprint value from Omaha response.
Store the unique fp value from response into prefs. Value is later sent
to Omaha to determine if there is a subsequent update available
while the system is waiting to be rebooted.
BUG=b:161259884
TEST=cros_workon_make --board=hatch --test update_engine
Change-Id: Ie37aa5da3cd8a0820e633f5ef426fb50e8a02838
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/2491618
Tested-by: Vyshu Khota <vyshu@google.com>
Commit-Queue: Vyshu Khota <vyshu@google.com>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/common/constants.cc b/common/constants.cc
index 8883668..a9cf238 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -71,6 +71,7 @@
const char kPrefsPingActive[] = "active";
const char kPrefsPingLastActive[] = "date_last_active";
const char kPrefsPingLastRollcall[] = "date_last_rollcall";
+const char kPrefsLastFp[] = "last-fp";
const char kPrefsPostInstallSucceeded[] = "post-install-succeeded";
const char kPrefsPreviousVersion[] = "previous-version";
const char kPrefsResumedUpdateFailures[] = "resumed-update-failures";
diff --git a/common/constants.h b/common/constants.h
index 3685102..1e97249 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -71,6 +71,7 @@
extern const char kPrefsPingActive[];
extern const char kPrefsPingLastActive[];
extern const char kPrefsPingLastRollcall[];
+extern const char kPrefsLastFp[];
extern const char kPrefsPostInstallSucceeded[];
extern const char kPrefsPreviousVersion[];
extern const char kPrefsResumedUpdateFailures[];
diff --git a/common/fake_prefs.cc b/common/fake_prefs.cc
index 73559c5..275667e 100644
--- a/common/fake_prefs.cc
+++ b/common/fake_prefs.cc
@@ -106,6 +106,22 @@
return true;
}
+bool FakePrefs::Delete(const string& key, const vector<string>& nss) {
+ bool success = Delete(key);
+ for (const auto& ns : nss) {
+ vector<string> ns_keys;
+ success = GetSubKeys(ns, &ns_keys) && success;
+ for (const auto& sub_key : ns_keys) {
+ auto last_key_seperator = sub_key.find_last_of(kKeySeparator);
+ if (last_key_seperator != string::npos &&
+ key == sub_key.substr(last_key_seperator + 1)) {
+ success = Delete(sub_key) && success;
+ }
+ }
+ }
+ return success;
+}
+
bool FakePrefs::GetSubKeys(const string& ns, vector<string>* keys) const {
for (const auto& pr : values_)
if (pr.first.compare(0, ns.length(), ns) == 0)
diff --git a/common/fake_prefs.h b/common/fake_prefs.h
index b24ff4d..9af2550 100644
--- a/common/fake_prefs.h
+++ b/common/fake_prefs.h
@@ -48,6 +48,8 @@
bool Exists(const std::string& key) const override;
bool Delete(const std::string& key) override;
+ bool Delete(const std::string& key,
+ const std::vector<std::string>& nss) override;
bool GetSubKeys(const std::string& ns,
std::vector<std::string>* keys) const override;
diff --git a/common/mock_prefs.h b/common/mock_prefs.h
index 62417a8..c91664e 100644
--- a/common/mock_prefs.h
+++ b/common/mock_prefs.h
@@ -41,6 +41,9 @@
MOCK_CONST_METHOD1(Exists, bool(const std::string& key));
MOCK_METHOD1(Delete, bool(const std::string& key));
+ MOCK_METHOD2(Delete,
+ bool(const std::string& key,
+ const std::vector<std::string>& nss));
MOCK_CONST_METHOD2(GetSubKeys,
bool(const std::string&, std::vector<std::string>*));
diff --git a/common/prefs.cc b/common/prefs.cc
index 615014f..52a58b7 100644
--- a/common/prefs.cc
+++ b/common/prefs.cc
@@ -34,8 +34,6 @@
namespace {
-const char kKeySeparator = '/';
-
void DeleteEmptyDirectories(const base::FilePath& path) {
base::FileEnumerator path_enum(
path, false /* recursive */, base::FileEnumerator::DIRECTORIES);
@@ -112,6 +110,24 @@
return true;
}
+bool PrefsBase::Delete(const string& pref_key, const vector<string>& nss) {
+ // Delete pref key for platform.
+ bool success = Delete(pref_key);
+ // Delete pref key in each namespace.
+ for (const auto& ns : nss) {
+ vector<string> namespace_keys;
+ success = GetSubKeys(ns, &namespace_keys) && success;
+ for (const auto& key : namespace_keys) {
+ auto last_key_seperator = key.find_last_of(kKeySeparator);
+ if (last_key_seperator != string::npos &&
+ pref_key == key.substr(last_key_seperator + 1)) {
+ success = Delete(key) && success;
+ }
+ }
+ }
+ return success;
+}
+
bool PrefsBase::GetSubKeys(const string& ns, vector<string>* keys) const {
return storage_->GetSubKeys(ns, keys);
}
diff --git a/common/prefs.h b/common/prefs.h
index 3fc1d89..d6ef668 100644
--- a/common/prefs.h
+++ b/common/prefs.h
@@ -74,6 +74,8 @@
bool Exists(const std::string& key) const override;
bool Delete(const std::string& key) override;
+ bool Delete(const std::string& pref_key,
+ const std::vector<std::string>& nss) override;
bool GetSubKeys(const std::string& ns,
std::vector<std::string>* keys) const override;
diff --git a/common/prefs_interface.h b/common/prefs_interface.h
index 1311cb4..866d0ca 100644
--- a/common/prefs_interface.h
+++ b/common/prefs_interface.h
@@ -80,6 +80,12 @@
// this key. Calling with non-existent keys does nothing.
virtual bool Delete(const std::string& key) = 0;
+ // Deletes the pref key from platform and given namespace subdirectories.
+ // Keys are matched against end of pref keys in each namespace.
+ // Returns true if all deletes were successful.
+ virtual bool Delete(const std::string& pref_key,
+ const std::vector<std::string>& nss) = 0;
+
// Creates a key which is part of a sub preference.
static std::string CreateSubKey(const std::vector<std::string>& ns_with_key);
@@ -98,6 +104,10 @@
// anymore for future Set*() and Delete() method calls.
virtual void RemoveObserver(const std::string& key,
ObserverInterface* observer) = 0;
+
+ protected:
+ // Key separator used to create sub key and get file names,
+ static const char kKeySeparator = '/';
};
} // namespace chromeos_update_engine
diff --git a/common/prefs_unittest.cc b/common/prefs_unittest.cc
index 6dd26c0..e8efd8a 100644
--- a/common/prefs_unittest.cc
+++ b/common/prefs_unittest.cc
@@ -118,6 +118,23 @@
for (const auto& key : keys0corner)
EXPECT_TRUE(common_prefs_->Delete(key));
EXPECT_FALSE(common_prefs_->Exists(key0corner));
+
+ // Test sub directory namespace.
+ const string kDlcPrefsSubDir = "foo-dir";
+ key1A = common_prefs_->CreateSubKey({kDlcPrefsSubDir, "dlc1", "keyA"});
+ EXPECT_TRUE(common_prefs_->SetString(key1A, "fp_1A"));
+ key1B = common_prefs_->CreateSubKey({kDlcPrefsSubDir, "dlc1", "keyB"});
+ EXPECT_TRUE(common_prefs_->SetString(key1B, "fp_1B"));
+ auto key2A = common_prefs_->CreateSubKey({kDlcPrefsSubDir, "dlc2", "keyA"});
+ EXPECT_TRUE(common_prefs_->SetString(key2A, "fp_A2"));
+
+ vector<string> fpKeys;
+ EXPECT_TRUE(common_prefs_->GetSubKeys(kDlcPrefsSubDir, &fpKeys));
+ EXPECT_EQ(fpKeys.size(), 3);
+ EXPECT_TRUE(common_prefs_->Delete(fpKeys[0]));
+ EXPECT_TRUE(common_prefs_->Delete(fpKeys[1]));
+ EXPECT_TRUE(common_prefs_->Delete(fpKeys[2]));
+ EXPECT_FALSE(common_prefs_->Exists(key1A));
}
PrefsInterface* common_prefs_;
@@ -423,6 +440,71 @@
EXPECT_FALSE(base::PathExists(prefs_dir_.Append(name_space)));
}
+TEST_F(PrefsTest, DeletePrefs) {
+ const string kPrefsSubDir = "foo-dir";
+ const string kFpKey = "kPrefFp";
+ const string kNotFpKey = "NotkPrefFp";
+ const string kOtherKey = "kPrefNotFp";
+
+ EXPECT_TRUE(prefs_.SetString(kFpKey, "3.000"));
+ EXPECT_TRUE(prefs_.SetString(kOtherKey, "not_fp_val"));
+
+ auto key1_fp = prefs_.CreateSubKey({kPrefsSubDir, "id-1", kFpKey});
+ EXPECT_TRUE(prefs_.SetString(key1_fp, "3.7"));
+ auto key_not_fp = prefs_.CreateSubKey({kPrefsSubDir, "id-1", kOtherKey});
+ EXPECT_TRUE(prefs_.SetString(key_not_fp, "not_fp_val"));
+ auto key2_fp = prefs_.CreateSubKey({kPrefsSubDir, "id-2", kFpKey});
+ EXPECT_TRUE(prefs_.SetString(key2_fp, "3.9"));
+ auto key3_fp = prefs_.CreateSubKey({kPrefsSubDir, "id-3", kFpKey});
+ EXPECT_TRUE(prefs_.SetString(key3_fp, "3.45"));
+
+ // Pref key does not match full subkey at end, should not delete.
+ auto key_middle_fp = prefs_.CreateSubKey({kPrefsSubDir, kFpKey, kOtherKey});
+ EXPECT_TRUE(prefs_.SetString(key_middle_fp, "not_fp_val"));
+ auto key_end_not_fp = prefs_.CreateSubKey({kPrefsSubDir, "id-1", kNotFpKey});
+ EXPECT_TRUE(prefs_.SetString(key_end_not_fp, "not_fp_val"));
+
+ // Delete key in platform and one namespace.
+ prefs_.Delete(kFpKey, {kPrefsSubDir});
+
+ EXPECT_FALSE(prefs_.Exists(kFpKey));
+ EXPECT_FALSE(prefs_.Exists(key1_fp));
+ EXPECT_FALSE(prefs_.Exists(key2_fp));
+ EXPECT_FALSE(prefs_.Exists(key3_fp));
+
+ // Check other keys are not deleted.
+ EXPECT_TRUE(prefs_.Exists(kOtherKey));
+ EXPECT_TRUE(prefs_.Exists(key_not_fp));
+ EXPECT_TRUE(prefs_.Exists(key_middle_fp));
+ EXPECT_TRUE(prefs_.Exists(key_end_not_fp));
+}
+
+TEST_F(PrefsTest, DeleteMultipleNamespaces) {
+ const string kFirstSubDir = "foo-dir";
+ const string kSecondarySubDir = "bar-dir";
+ const string kTertiarySubDir = "ter-dir";
+ const string kFpKey = "kPrefFp";
+
+ EXPECT_TRUE(prefs_.SetString(kFpKey, "3.000"));
+ // Set pref key in different namespaces.
+ auto key1_fp = prefs_.CreateSubKey({kFirstSubDir, "id-1", kFpKey});
+ EXPECT_TRUE(prefs_.SetString(key1_fp, "3.7"));
+ auto key2_fp = prefs_.CreateSubKey({kSecondarySubDir, "id-3", kFpKey});
+ EXPECT_TRUE(prefs_.SetString(key2_fp, "7.45"));
+ auto key3_fp = prefs_.CreateSubKey({kTertiarySubDir, "id-3", kFpKey});
+ EXPECT_TRUE(prefs_.SetString(key3_fp, "7.45"));
+
+ // Delete key in platform and given namespaces.
+ prefs_.Delete(kFpKey, {kFirstSubDir, kSecondarySubDir});
+
+ EXPECT_FALSE(prefs_.Exists(kFpKey));
+ EXPECT_FALSE(prefs_.Exists(key1_fp));
+ EXPECT_FALSE(prefs_.Exists(key2_fp));
+
+ // Tertiary namespace not given to delete. Key should still exist.
+ EXPECT_TRUE(prefs_.Exists(key3_fp));
+}
+
class MockPrefsObserver : public PrefsInterface::ObserverInterface {
public:
MOCK_METHOD1(OnPrefSet, void(const string&));
diff --git a/cros/omaha_request_action.cc b/cros/omaha_request_action.cc
index faa7dde..cad0c67 100644
--- a/cros/omaha_request_action.cc
+++ b/cros/omaha_request_action.cc
@@ -98,6 +98,7 @@
constexpr char kAttrElapsedDays[] = "elapsed_days";
constexpr char kAttrElapsedSeconds[] = "elapsed_seconds";
constexpr char kAttrEvent[] = "event";
+constexpr char kAttrFp[] = "fp";
constexpr char kAttrHashSha256[] = "hash_sha256";
// Deprecated: "hash"; Although we still need to pass it from the server for
// backward compatibility.
@@ -150,6 +151,7 @@
string name;
string size;
string hash;
+ string fp;
};
vector<Package> packages;
};
@@ -214,7 +216,8 @@
if (!data->apps.empty())
data->apps.back().packages.push_back({.name = attrs[kAttrName],
.size = attrs[kAttrSize],
- .hash = attrs[kAttrHashSha256]});
+ .hash = attrs[kAttrHashSha256],
+ .fp = attrs[kAttrFp]});
} else if (data->current_path == "/response/app/updatecheck/manifest") {
// Get the version.
if (!data->apps.empty())
@@ -612,6 +615,8 @@
return false;
}
+ out_package.fp = package.fp;
+
if (i < is_delta_payloads.size())
out_package.is_delta = ParseBool(is_delta_payloads[i]);
diff --git a/cros/omaha_request_action_unittest.cc b/cros/omaha_request_action_unittest.cc
index 8d94195..9f9c75f 100644
--- a/cros/omaha_request_action_unittest.cc
+++ b/cros/omaha_request_action_unittest.cc
@@ -158,10 +158,10 @@
version +
"\">"
"<packages><package hash=\"not-used\" name=\"" +
- filename + "\" size=\"" + base::NumberToString(size) +
- "\" hash_sha256=\"" + hash + "\"/>" +
- (multi_package ? "<package name=\"package2\" size=\"222\" "
- "hash_sha256=\"hash2\"/>"
+ filename + "\" size=\"" + base::NumberToString(size) + "\" fp=\"" +
+ fp + "\" hash_sha256=\"" + hash + "\"/>" +
+ (multi_package ? "<package name=\"package2\" size=\"222\" fp=\"" +
+ fp2 + "\" hash_sha256=\"hash2\"/>"
: "") +
"</packages>"
"<actions><action event=\"postinstall\" MetadataSize=\"11" +
@@ -187,8 +187,9 @@
"><updatecheck status=\"ok\"><urls><url codebase=\"" +
codebase2 + "\"/></urls><manifest version=\"" + version2 +
"\"><packages>"
- "<package name=\"package3\" size=\"333\" "
- "hash_sha256=\"hash3\"/></packages>"
+ "<package name=\"package3\" size=\"333\" fp=\"" +
+ fp2 +
+ "\" hash_sha256=\"hash3\"/></packages>"
"<actions><action event=\"postinstall\" " +
(multi_app_self_update
? "noupdate=\"true\" IsDeltaPayload=\"true\" "
@@ -215,8 +216,10 @@
codebase + "\"/><url codebase=\"" + codebase2 +
"\"/></urls><manifest version=\"" + version +
"\"><packages><package name=\"package3\" size=\"333\" "
- "hash_sha256=\"hash3\"/></packages><actions>"
- "<action event=\"install\" run=\".signed\"/>"
+ "fp=\"" +
+ fp2 +
+ "\" hash_sha256=\"hash3\"/></packages>"
+ "<actions><action event=\"install\" run=\".signed\"/>"
"<action event=\"postinstall\" MetadataSize=\"33\"/>"
"</actions></manifest></updatecheck></app>"
: "") +
@@ -248,6 +251,8 @@
string codebase2 = "http://code/base/2/";
string filename = "file.signed";
string hash = "4841534831323334";
+ string fp = "3.98ba213e";
+ string fp2 = "3.755aff78e";
uint64_t size = 123;
string deadline = "";
string max_days_to_scatter = "7";
@@ -670,6 +675,7 @@
EXPECT_EQ(fake_update_response_.more_info_url, response.more_info_url);
EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+ EXPECT_EQ(fake_update_response_.fp, response.packages[0].fp);
EXPECT_EQ(true, response.packages[0].is_delta);
EXPECT_EQ(fake_update_response_.prompt == "true", response.prompt);
EXPECT_EQ(fake_update_response_.deadline, response.deadline);
@@ -695,11 +701,13 @@
response.packages[1].payload_urls[0]);
EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+ EXPECT_EQ(fake_update_response_.fp, response.packages[0].fp);
EXPECT_EQ(true, response.packages[0].is_delta);
EXPECT_EQ(11u, response.packages[0].metadata_size);
ASSERT_EQ(2u, response.packages.size());
EXPECT_EQ(string("hash2"), response.packages[1].hash);
EXPECT_EQ(222u, response.packages[1].size);
+ EXPECT_EQ(fake_update_response_.fp2, response.packages[1].fp);
EXPECT_EQ(22u, response.packages[1].metadata_size);
EXPECT_EQ(false, response.packages[1].is_delta);
}
@@ -718,11 +726,13 @@
response.packages[1].payload_urls[0]);
EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+ EXPECT_EQ(fake_update_response_.fp, response.packages[0].fp);
EXPECT_EQ(11u, response.packages[0].metadata_size);
EXPECT_EQ(true, response.packages[0].is_delta);
ASSERT_EQ(2u, response.packages.size());
EXPECT_EQ(string("hash3"), response.packages[1].hash);
EXPECT_EQ(333u, response.packages[1].size);
+ EXPECT_EQ(fake_update_response_.fp2, response.packages[1].fp);
EXPECT_EQ(33u, response.packages[1].metadata_size);
EXPECT_EQ(false, response.packages[1].is_delta);
}
@@ -740,10 +750,12 @@
response.packages[0].payload_urls[0]);
EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+ EXPECT_EQ(fake_update_response_.fp, response.packages[0].fp);
EXPECT_EQ(11u, response.packages[0].metadata_size);
ASSERT_EQ(2u, response.packages.size());
EXPECT_EQ(string("hash3"), response.packages[1].hash);
EXPECT_EQ(333u, response.packages[1].size);
+ EXPECT_EQ(fake_update_response_.fp2, response.packages[1].fp);
EXPECT_EQ(33u, response.packages[1].metadata_size);
EXPECT_EQ(true, response.packages[1].is_delta);
}
@@ -765,15 +777,18 @@
response.packages[2].payload_urls[0]);
EXPECT_EQ(fake_update_response_.hash, response.packages[0].hash);
EXPECT_EQ(fake_update_response_.size, response.packages[0].size);
+ EXPECT_EQ(fake_update_response_.fp, response.packages[0].fp);
EXPECT_EQ(11u, response.packages[0].metadata_size);
EXPECT_EQ(true, response.packages[0].is_delta);
ASSERT_EQ(3u, response.packages.size());
EXPECT_EQ(string("hash2"), response.packages[1].hash);
EXPECT_EQ(222u, response.packages[1].size);
+ EXPECT_EQ(fake_update_response_.fp2, response.packages[1].fp);
EXPECT_EQ(22u, response.packages[1].metadata_size);
EXPECT_EQ(false, response.packages[1].is_delta);
EXPECT_EQ(string("hash3"), response.packages[2].hash);
EXPECT_EQ(333u, response.packages[2].size);
+ EXPECT_EQ(fake_update_response_.fp2, response.packages[2].fp);
EXPECT_EQ(33u, response.packages[2].metadata_size);
EXPECT_EQ(false, response.packages[2].is_delta);
}
@@ -1557,7 +1572,7 @@
"<urls><url codebase=\"http://missing/field/test/\"/></urls>"
"<manifest version=\"10.2.3.4\">"
"<packages><package hash=\"not-used\" name=\"f\" "
- "size=\"587\" hash_sha256=\"lkq34j5345\"/></packages>"
+ "size=\"587\" fp=\"3.789\" hash_sha256=\"lkq34j5345\"/></packages>"
"<actions><action event=\"postinstall\" "
"Prompt=\"false\" "
"IsDeltaPayload=\"false\" "
@@ -1572,6 +1587,7 @@
response.packages[0].payload_urls[0]);
EXPECT_EQ("", response.more_info_url);
EXPECT_EQ("lkq34j5345", response.packages[0].hash);
+ EXPECT_EQ(string("3.789"), response.packages[0].fp);
EXPECT_EQ(587u, response.packages[0].size);
EXPECT_FALSE(response.prompt);
EXPECT_TRUE(response.deadline.empty());
diff --git a/cros/omaha_response.h b/cros/omaha_response.h
index 43783d6..3b07745 100644
--- a/cros/omaha_response.h
+++ b/cros/omaha_response.h
@@ -55,6 +55,8 @@
bool can_exclude = false;
// The App ID associated with the package.
std::string app_id;
+ // The unique fingerprint value associated with the package.
+ std::string fp;
};
std::vector<Package> packages;
diff --git a/cros/omaha_response_handler_action.cc b/cros/omaha_response_handler_action.cc
index b6c223f..52142a3 100644
--- a/cros/omaha_response_handler_action.cc
+++ b/cros/omaha_response_handler_action.cc
@@ -106,7 +106,9 @@
.metadata_signature = package.metadata_signature,
.hash = raw_hash,
.type = package.is_delta ? InstallPayloadType::kDelta
- : InstallPayloadType::kFull});
+ : InstallPayloadType::kFull,
+ .fp = package.fp,
+ .app_id = package.app_id});
update_check_response_hash += package.hash + ":";
}
install_plan_.public_key_rsa = response.public_key_rsa;
diff --git a/cros/omaha_response_handler_action_unittest.cc b/cros/omaha_response_handler_action_unittest.cc
index b05660c..74f4d04 100644
--- a/cros/omaha_response_handler_action_unittest.cc
+++ b/cros/omaha_response_handler_action_unittest.cc
@@ -117,6 +117,9 @@
"-the_update_a.b.c.d_DELTA_.tgz";
const char* const kBadVersion = "don't update me";
const char* const kPayloadHashHex = "486173682b";
+const char* const kPayloadFp1 = "1.755aff78ec73dfc7f590893ac";
+const char* const kPayloadFp2 = "1.98ba213e0ccec0d0e8cdc74a5";
+const char* const kPayloadAppId = "test_app_id";
} // namespace
bool OmahaResponseHandlerActionTest::DoTest(const OmahaResponse& in,
@@ -185,7 +188,9 @@
in.packages.push_back(
{.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
.size = 12,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.more_info_url = "http://more/info";
in.prompt = false;
in.deadline = "20101020";
@@ -193,6 +198,8 @@
EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_EQ(1U, install_plan.target_slot);
string deadline;
EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
@@ -211,7 +218,9 @@
in.packages.push_back(
{.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
.size = 12,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.more_info_url = "http://more/info";
in.prompt = true;
InstallPlan install_plan;
@@ -220,6 +229,8 @@
EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_EQ(0U, install_plan.target_slot);
string deadline;
EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline) &&
@@ -230,8 +241,11 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.packages.push_back(
- {.payload_urls = {kLongName}, .size = 12, .hash = kPayloadHashHex});
+ in.packages.push_back({.payload_urls = {kLongName},
+ .size = 12,
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.more_info_url = "http://more/info";
in.prompt = true;
in.deadline = "some-deadline";
@@ -245,6 +259,8 @@
EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_EQ(1U, install_plan.target_slot);
string deadline;
EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
@@ -255,8 +271,11 @@
OmahaResponse in;
in.update_exists = true;
in.version = "a.b.c.d";
- in.packages.push_back(
- {.payload_urls = {kLongName}, .size = 12, .hash = kPayloadHashHex});
+ in.packages.push_back({.payload_urls = {kLongName},
+ .size = 12,
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.more_info_url = "http://more/info";
in.prompt = true;
in.deadline = "some-deadline";
@@ -268,6 +287,8 @@
EXPECT_TRUE(DoTest(in, test_deadline_file.path(), &install_plan));
EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_EQ(1U, install_plan.target_slot);
string deadline;
EXPECT_TRUE(utils::ReadFile(test_deadline_file.path(), &deadline));
@@ -309,10 +330,14 @@
in.version = "a.b.c.d";
in.packages.push_back({.payload_urls = {"http://package/1"},
.size = 1,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.packages.push_back({.payload_urls = {"http://package/2"},
.size = 2,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp2});
in.more_info_url = "http://more/info";
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "", &install_plan));
@@ -322,6 +347,10 @@
EXPECT_EQ(in.packages[1].size, install_plan.payloads[1].size);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
EXPECT_EQ(expected_hash_, install_plan.payloads[1].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[1].app_id, install_plan.payloads[1].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
+ EXPECT_EQ(in.packages[1].fp, install_plan.payloads[1].fp);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -332,7 +361,9 @@
in.packages.push_back(
{.payload_urls = {"http://test.should/need/hash.checks.signed"},
.size = 12,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.more_info_url = "http://more/info";
// Hash checks are always skipped for non-official update URLs.
EXPECT_CALL(*(fake_system_state_.mock_request_params()),
@@ -342,6 +373,8 @@
EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_TRUE(install_plan.hash_checks_mandatory);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -353,7 +386,9 @@
in.packages.push_back(
{.payload_urls = {"http://url.normally/needs/hash.checks.signed"},
.size = 12,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.more_info_url = "http://more/info";
EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
@@ -362,6 +397,8 @@
EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_FALSE(install_plan.hash_checks_mandatory);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -375,7 +412,9 @@
in.packages.push_back(
{.payload_urls = {"http://url.normally/needs/hash.checks.signed"},
.size = 12,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.more_info_url = "http://more/info";
EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
@@ -385,6 +424,8 @@
EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_FALSE(install_plan.hash_checks_mandatory);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -396,7 +437,9 @@
in.packages.push_back(
{.payload_urls = {"https://test.should/need/hash.checks.signed"},
.size = 12,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.more_info_url = "http://more/info";
EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
@@ -405,6 +448,8 @@
EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_TRUE(install_plan.hash_checks_mandatory);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -417,7 +462,9 @@
{.payload_urls = {"http://test.should.still/need/hash.checks",
"https://test.should.still/need/hash.checks"},
.size = 12,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.more_info_url = "http://more/info";
EXPECT_CALL(*(fake_system_state_.mock_request_params()),
IsUpdateUrlOfficial())
@@ -426,6 +473,8 @@
EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_TRUE(install_plan.hash_checks_mandatory);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -675,7 +724,9 @@
in.packages.push_back(
{.payload_urls = {"https://would.not/cause/hash/checks"},
.size = 12,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.more_info_url = "http://more/info";
OmahaRequestParams params(&fake_system_state_);
@@ -698,6 +749,8 @@
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "", &install_plan));
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_EQ(p2p_url, install_plan.download_url);
EXPECT_TRUE(install_plan.hash_checks_mandatory);
}
@@ -899,10 +952,14 @@
in.version = "a.b.c.d";
in.packages.push_back({.payload_urls = {"http://package/1"},
.size = 1,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
in.packages.push_back({.payload_urls = {"http://package/2"},
.size = 2,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp2});
in.more_info_url = "http://more/info";
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "", &install_plan));
@@ -912,6 +969,10 @@
EXPECT_EQ(in.packages[1].size, install_plan.payloads[1].size);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
EXPECT_EQ(expected_hash_, install_plan.payloads[1].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[1].app_id, install_plan.payloads[1].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
+ EXPECT_EQ(in.packages[1].fp, install_plan.payloads[1].fp);
EXPECT_EQ(in.version, install_plan.version);
}
@@ -921,7 +982,9 @@
in.version = "a.b.c.d";
in.packages.push_back({.payload_urls = {"http://foo/the_update_a.b.c.d.tgz"},
.size = 12,
- .hash = kPayloadHashHex});
+ .hash = kPayloadHashHex,
+ .app_id = kPayloadAppId,
+ .fp = kPayloadFp1});
// Setup the UpdateManager to disallow the update.
FakeClock fake_clock;
MockPolicy* mock_policy = new MockPolicy(&fake_clock);
@@ -942,6 +1005,8 @@
install_plan = *delegate_.response_handler_action_install_plan_;
EXPECT_EQ(in.packages[0].payload_urls[0], install_plan.download_url);
EXPECT_EQ(expected_hash_, install_plan.payloads[0].hash);
+ EXPECT_EQ(in.packages[0].app_id, install_plan.payloads[0].app_id);
+ EXPECT_EQ(in.packages[0].fp, install_plan.payloads[0].fp);
EXPECT_EQ(1U, install_plan.target_slot);
EXPECT_EQ(in.version, install_plan.version);
}
diff --git a/cros/update_attempter.cc b/cros/update_attempter.cc
index e417457..5c21d04 100644
--- a/cros/update_attempter.cc
+++ b/cros/update_attempter.cc
@@ -158,10 +158,12 @@
// In case of update_engine restart without a reboot we need to restore the
// reboot needed state.
- if (GetBootTimeAtUpdate(nullptr))
+ if (GetBootTimeAtUpdate(nullptr)) {
status_ = UpdateStatus::UPDATED_NEED_REBOOT;
- else
+ } else {
status_ = UpdateStatus::IDLE;
+ prefs_->Delete(kPrefsLastFp, {kDlcPrefsSubDir});
+ }
}
bool UpdateAttempter::ScheduleUpdates() {
@@ -646,6 +648,20 @@
return failures.size() == 0;
}
+void UpdateAttempter::SetPref(const string& pref_key,
+ const string& pref_value,
+ const string& payload_id) {
+ string dlc_id;
+ if (!omaha_request_params_->GetDlcId(payload_id, &dlc_id)) {
+ // Not a DLC ID, set fingerprint in perf for platform ID.
+ prefs_->SetString(pref_key, pref_value);
+ } else {
+ // Set fingerprint in pref for DLC ID.
+ auto key = prefs_->CreateSubKey({kDlcPrefsSubDir, dlc_id, pref_key});
+ prefs_->SetString(key, pref_value);
+ }
+}
+
bool UpdateAttempter::SetDlcActiveValue(bool is_active, const string& dlc_id) {
if (dlc_id.empty()) {
LOG(ERROR) << "Empty DLC ID passed.";
@@ -1198,6 +1214,9 @@
for (const auto& payload : install_plan_->payloads) {
target_version_uid += brillo::data_encoding::Base64Encode(payload.hash) +
":" + payload.metadata_signature + ":";
+ // Set fingerprint value for updates only.
+ if (!is_install_)
+ SetPref(kPrefsLastFp, payload.fp, payload.app_id);
}
// If we just downloaded a rollback image, we should preserve this fact
@@ -1419,6 +1438,7 @@
// UpdateStatus::UPDATED_NEED_REBOOT state.
ret_value = prefs_->Delete(kPrefsUpdateCompletedOnBootId) && ret_value;
ret_value = prefs_->Delete(kPrefsUpdateCompletedBootTime) && ret_value;
+ ret_value = prefs_->Delete(kPrefsLastFp, {kDlcPrefsSubDir}) && ret_value;
// Update the boot flags so the current slot has higher priority.
BootControlInterface* boot_control = system_state_->boot_control();
diff --git a/cros/update_attempter.h b/cros/update_attempter.h
index bd0aef6..a201acf 100644
--- a/cros/update_attempter.h
+++ b/cros/update_attempter.h
@@ -433,6 +433,11 @@
// Resets all the DLC prefs.
bool ResetDlcPrefs(const std::string& dlc_id);
+ // Sets given pref key for DLC and platform.
+ void SetPref(const std::string& pref_key,
+ const std::string& pref_value,
+ const std::string& payload_id);
+
// Get the integer values from the DLC metadata for |kPrefsPingLastActive|
// or |kPrefsPingLastRollcall|.
// The value is equal to -2 when the value cannot be read or is not numeric.
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 4a37836..3aae663 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -72,13 +72,16 @@
for (const auto& payload : payloads) {
payloads_str += base::StringPrintf(
", payload: (urls: %s, size: %" PRIu64 ", metadata_size: %" PRIu64
- ", metadata signature: %s, hash: %s, payload type: %s)",
+ ", metadata signature: %s, hash: %s, payload type: %s"
+ ", fingerprint: %s, app id: %s)",
PayloadUrlsToString(payload.payload_urls).c_str(),
payload.size,
payload.metadata_size,
payload.metadata_signature.c_str(),
base::HexEncode(payload.hash.data(), payload.hash.size()).c_str(),
- InstallPayloadTypeToString(payload.type).c_str());
+ InstallPayloadTypeToString(payload.type).c_str(),
+ payload.fp.c_str(),
+ payload.app_id.c_str());
}
string version_str = base::StringPrintf(", version: %s", version.c_str());
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index ee1a72b..16e5674 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -62,6 +62,8 @@
std::string metadata_signature; // signature of the metadata in base64
brillo::Blob hash; // SHA256 hash of the payload
InstallPayloadType type{InstallPayloadType::kUnknown};
+ std::string fp; // fingerprint value unique to the payload
+ std::string app_id; // App ID of the payload
// Only download manifest and fill in partitions in install plan without
// apply the payload if true. Will be set by DownloadAction when resuming
// multi-payload.
@@ -72,7 +74,8 @@
metadata_size == that.metadata_size &&
metadata_signature == that.metadata_signature &&
hash == that.hash && type == that.type &&
- already_applied == that.already_applied;
+ already_applied == that.already_applied && fp == that.fp &&
+ app_id == that.app_id;
}
};
std::vector<Payload> payloads;