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&));