update_engine: Add PayloadState Exclusion Logic
|PayloadState| will exclude payloads based on specific update failures.
This is to prevent critical platform updates from being blocked by less
critical updates (e.g. DLCs). A layer of robustness is added in
protecting CrOS devices from falling off the update train.
Some important changes to mention:
- Only during updates will update_engine exclude non-critical payloads
- |OmahaRequestAction|, the current precursor |Action| to
|OmahaResponseHandlerAction|, during a update will exclude
faulty/excluded payloads prior to setting the |OmahaResponse| as an
output object for suqsequent bonded |Action| to consume
- When all payloads are excluded for an update, the |ErrorCode| will
be indicated as |OmahaResponseInvalid| as this case is not a valid
Omaha response update_engine should ever run into because non-critical
updates must tag alongside a critical update
BUG=chromium:928805
TEST=FEATURES=test emerge-$B update_engine update_engine-client
Change-Id: I0551a228d0b84defb4d59966e8ed46a5d9278d60
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/2190237
Tested-by: Jae Hoon Kim <kimjae@chromium.org>
Auto-Submit: Jae Hoon Kim <kimjae@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
Commit-Queue: Jae Hoon Kim <kimjae@chromium.org>
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index e530de4..6a0c213 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -44,6 +44,7 @@
#include "update_engine/common/constants.h"
#include "update_engine/common/fake_prefs.h"
#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/mock_excluder.h"
#include "update_engine/common/mock_http_fetcher.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/common/prefs.h"
@@ -75,6 +76,7 @@
using testing::ReturnRef;
using testing::SaveArg;
using testing::SetArgPointee;
+using testing::StrictMock;
namespace {
@@ -204,7 +206,8 @@
? "<app appid=\"" + request_params.GetDlcAppId(kDlcId1) +
"\" status=\"ok\">"
"<updatecheck status=\"ok\"><urls><url codebase=\"" +
- codebase + "\"/></urls><manifest version=\"" + version +
+ codebase + "\"/><url codebase=\"" + codebase2 +
+ "\"/></urls><manifest version=\"" + version +
"\"><packages><package name=\"package3\" size=\"333\" "
"hash_sha256=\"hash3\"/></packages><actions>"
"<action event=\"install\" run=\".signed\"/>"
@@ -389,6 +392,9 @@
.expected_check_reaction = metrics::CheckReaction::kUpdating,
.expected_download_error_code = metrics::DownloadErrorCode::kUnset,
};
+
+ ON_CALL(*fake_system_state_.mock_update_attempter(), GetExcluder())
+ .WillByDefault(Return(&mock_excluder_));
}
// This function uses the parameters in |tuc_params_| to do an update check.
@@ -429,6 +435,7 @@
bool expected_allow_p2p_for_sharing,
const string& expected_p2p_url);
+ StrictMock<MockExcluder> mock_excluder_;
FakeSystemState fake_system_state_;
FakeUpdateResponse fake_update_response_;
// Used by all tests.
@@ -2759,8 +2766,44 @@
{{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
fake_update_response_.dlc_app_update = true;
tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
+ EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
ASSERT_TRUE(TestUpdateCheck());
+ EXPECT_EQ(response.packages.size(), 2u);
+ // Two candidate URLs.
+ EXPECT_EQ(response.packages[1].payload_urls.size(), 2u);
+ EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, UpdateWithPartiallyExcludedDlcTest) {
+ request_params_.set_dlc_apps_params(
+ {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
+ fake_update_response_.dlc_app_update = true;
+ tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
+ // The first DLC candidate URL is excluded.
+ EXPECT_CALL(mock_excluder_, IsExcluded(_))
+ .WillOnce(Return(true))
+ .WillOnce(Return(false));
+ ASSERT_TRUE(TestUpdateCheck());
+
+ EXPECT_EQ(response.packages.size(), 2u);
+ // One candidate URL.
+ EXPECT_EQ(response.packages[1].payload_urls.size(), 1u);
+ EXPECT_TRUE(response.update_exists);
+}
+
+TEST_F(OmahaRequestActionTest, UpdateWithExcludedDlcTest) {
+ request_params_.set_dlc_apps_params(
+ {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
+ fake_update_response_.dlc_app_update = true;
+ tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
+ // Both DLC candidate URLs are excluded.
+ EXPECT_CALL(mock_excluder_, IsExcluded(_))
+ .WillOnce(Return(true))
+ .WillOnce(Return(true));
+ ASSERT_TRUE(TestUpdateCheck());
+
+ EXPECT_EQ(response.packages.size(), 1u);
EXPECT_TRUE(response.update_exists);
}
@@ -2769,6 +2812,7 @@
{{request_params_.GetDlcAppId(kDlcId2), {.name = kDlcId2}}});
fake_update_response_.dlc_app_no_update = true;
tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
+ EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
ASSERT_TRUE(TestUpdateCheck());
EXPECT_TRUE(response.update_exists);
@@ -2781,6 +2825,7 @@
fake_update_response_.dlc_app_update = true;
fake_update_response_.dlc_app_no_update = true;
tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
+ EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
ASSERT_TRUE(TestUpdateCheck());
EXPECT_TRUE(response.update_exists);
@@ -2991,4 +3036,37 @@
EXPECT_EQ(temp_str, "4763");
}
+TEST_F(OmahaRequestActionTest, OmahaResponseUpdateCanExcludeCheck) {
+ request_params_.set_dlc_apps_params(
+ {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
+ fake_update_response_.dlc_app_update = true;
+ tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
+
+ EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
+ ASSERT_TRUE(TestUpdateCheck());
+ ASSERT_TRUE(delegate_.omaha_response_);
+ const auto& packages = delegate_.omaha_response_->packages;
+ ASSERT_EQ(packages.size(), 2);
+
+ EXPECT_FALSE(packages[0].can_exclude);
+ EXPECT_TRUE(packages[1].can_exclude);
+}
+
+TEST_F(OmahaRequestActionTest, OmahaResponseInstallCannotExcludeCheck) {
+ request_params_.set_is_install(true);
+ request_params_.set_dlc_apps_params(
+ {{request_params_.GetDlcAppId(kDlcId1), {.name = kDlcId1}}});
+ fake_update_response_.dlc_app_update = true;
+ tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
+
+ EXPECT_CALL(mock_excluder_, IsExcluded(_)).WillRepeatedly(Return(false));
+ ASSERT_TRUE(TestUpdateCheck());
+ ASSERT_TRUE(delegate_.omaha_response_);
+ const auto& packages = delegate_.omaha_response_->packages;
+ ASSERT_EQ(packages.size(), 2);
+
+ EXPECT_FALSE(packages[0].can_exclude);
+ EXPECT_FALSE(packages[1].can_exclude);
+}
+
} // namespace chromeos_update_engine