update_engine: Add ping for DLCs in update_engine
Send ping to omaha with the metadata values 'active','date_last_active'
and 'date_last_rollcall'. Update engine resets the 'active' flag after
succesfully sending a ping to Omaha. The 'date_last_active' value is
sent and updated only when the DLC was active since the previous ping.
'date_last_rollcall' is sent on every ping.
BUG=chromium:912666
TEST=unittests
TEST=Test on DUT using Nebraska and forcing ping values by changing the
metadata files in /var/lib/dlc/dummy-dlc/.
Installed dlc using:dlcservice_util --dlc_ids="dummy-dlc" --install
Trigger the pings by calling: update_engine_client --check_for_update
Change-Id: I47eff8c7923f5b3a7e892c281933c9a12b619ee7
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/2001095
Tested-by: Andrew Lassalle <andrewlassalle@chromium.org>
Commit-Queue: Andrew Lassalle <andrewlassalle@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/common/constants.cc b/common/constants.cc
index d779dd4..58cf1b3 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -18,6 +18,9 @@
namespace chromeos_update_engine {
+// Keep this in sync with the one in dlcservice.
+const char kDlcMetadataRootpath[] = "/var/lib/dlc/";
+
const char kPowerwashSafePrefsSubDirectory[] = "update_engine/prefs";
const char kPrefsSubDirectory[] = "prefs";
@@ -60,6 +63,11 @@
const char kPrefsP2PFirstAttemptTimestamp[] = "p2p-first-attempt-timestamp";
const char kPrefsP2PNumAttempts[] = "p2p-num-attempts";
const char kPrefsPayloadAttemptNumber[] = "payload-attempt-number";
+// Keep |kPrefsPingActive| in sync with |kDlcMetadataFilePingActive| in
+// dlcservice.
+const char kPrefsPingActive[] = "active";
+const char kPrefsPingLastActive[] = "date_last_active";
+const char kPrefsPingLastRollcall[] = "date_last_rollcall";
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 8685f7e..44b20b0 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -19,6 +19,10 @@
namespace chromeos_update_engine {
+// The root path of all DLC modules metadata.
+// Keep this in sync with the one in dlcservice.
+extern const char kDlcMetadataRootpath[];
+
// Directory for AU prefs that are preserved across powerwash.
extern const char kPowerwashSafePrefsSubDirectory[];
@@ -61,6 +65,9 @@
extern const char kPrefsP2PFirstAttemptTimestamp[];
extern const char kPrefsP2PNumAttempts[];
extern const char kPrefsPayloadAttemptNumber[];
+extern const char kPrefsPingActive[];
+extern const char kPrefsPingLastActive[];
+extern const char kPrefsPingLastRollcall[];
extern const char kPrefsPostInstallSucceeded[];
extern const char kPrefsPreviousVersion[];
extern const char kPrefsResumedUpdateFailures[];
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index f25f8ee..b6b4356 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -26,6 +26,7 @@
#include <vector>
#include <base/bind.h>
+#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/rand_util.h>
#include <base/strings/string_number_conversions.h>
@@ -43,6 +44,7 @@
#include "update_engine/common/hardware_interface.h"
#include "update_engine/common/hash_calculator.h"
#include "update_engine/common/platform_constants.h"
+#include "update_engine/common/prefs.h"
#include "update_engine/common/prefs_interface.h"
#include "update_engine/common/utils.h"
#include "update_engine/connection_manager_interface.h"
@@ -299,7 +301,7 @@
// Calculates the value to use for the ping days parameter.
int OmahaRequestAction::CalculatePingDays(const string& key) {
- int days = kNeverPinged;
+ int days = kPingNeverPinged;
int64_t last_ping = 0;
if (system_state_->prefs()->GetInt64(key, &last_ping) && last_ping >= 0) {
days = (Time::Now() - Time::FromInternalValue(last_ping)).InDays();
@@ -330,8 +332,8 @@
}
bool OmahaRequestAction::ShouldPing() const {
- if (ping_active_days_ == kNeverPinged &&
- ping_roll_call_days_ == kNeverPinged) {
+ if (ping_active_days_ == kPingNeverPinged &&
+ ping_roll_call_days_ == kPingNeverPinged) {
int powerwash_count = system_state_->hardware()->GetPowerwashCount();
if (powerwash_count > 0) {
LOG(INFO) << "Not sending ping with a=-1 r=-1 to omaha because "
@@ -412,6 +414,59 @@
return num_days;
}
+// static
+void OmahaRequestAction::StorePingReply(
+ const OmahaParserData& parser_data) const {
+ for (const auto& app : parser_data.apps) {
+ auto it = params_->dlc_apps_params().find(app.id);
+ if (it == params_->dlc_apps_params().end())
+ continue;
+
+ const OmahaRequestParams::AppParams& dlc_params = it->second;
+
+ // Skip if the ping for this DLC was not sent.
+ if (!dlc_params.send_ping)
+ continue;
+
+ base::FilePath metadata_path =
+ base::FilePath(params_->dlc_prefs_root()).Append(dlc_params.name);
+ if (!base::PathExists(metadata_path)) {
+ LOG(ERROR) << "Metadata path (" << metadata_path.value() << ") "
+ << "doesn't exist.";
+ // Skip this DLC if the metadata directory is missing.
+ continue;
+ }
+
+ Prefs prefs;
+ if (!prefs.Init(metadata_path)) {
+ LOG(ERROR) << "Failed to initialize the preferences path:"
+ << metadata_path.value() << ".";
+ continue;
+ }
+ // Reset the active metadata value to |kPingInactiveValue|.
+ // Only write into this file if the file exists, otherwise the file will be
+ // created with different owner/permissions.
+ if (prefs.Exists(kPrefsPingActive) &&
+ !prefs.SetInt64(kPrefsPingActive, kPingInactiveValue))
+ LOG(ERROR) << "Failed to set the value of ping metadata '"
+ << kPrefsPingActive << "'.";
+
+ if (!prefs.SetString(kPrefsPingLastRollcall,
+ parser_data.daystart_elapsed_days))
+ LOG(ERROR) << "Failed to set the value of ping metadata '"
+ << kPrefsPingLastRollcall << "'.";
+
+ if (dlc_params.ping_active) {
+ // Write the value of elapsed_days into |kPrefsPingLastActive| only if
+ // the previous ping was an active one.
+ if (!prefs.SetString(kPrefsPingLastActive,
+ parser_data.daystart_elapsed_days))
+ LOG(ERROR) << "Failed to set the value of ping metadata '"
+ << kPrefsPingLastActive << "'.";
+ }
+ }
+}
+
void OmahaRequestAction::PerformAction() {
http_fetcher_->set_delegate(this);
InitPingDays();
@@ -922,6 +977,9 @@
}
}
+ // Create/update the metadata files for each DLC app received.
+ StorePingReply(parser_data);
+
if (!HasOutputPipe()) {
// Just set success to whether or not the http transfer succeeded,
// which must be true at this point in the code.
diff --git a/omaha_request_action.h b/omaha_request_action.h
index 3f66de9..623a704 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -200,6 +200,10 @@
// send to Omaha and thus we should include them in the response.
bool ShouldPing() const;
+ // Process Omaha's response to a ping request and store the results in the DLC
+ // metadata directory.
+ void StorePingReply(const OmahaParserData& parser_data) const;
+
// Returns true if the download of a new update should be deferred.
// False if the update can be downloaded.
bool ShouldDeferDownload(OmahaResponse* output_object);
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 7b676e2..d1cb4ed 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -35,6 +35,7 @@
#include <brillo/message_loops/fake_message_loop.h>
#include <brillo/message_loops/message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
+#include <expat.h>
#include <gtest/gtest.h>
#include <policy/libpolicy.h>
#include <policy/mock_libpolicy.h>
@@ -347,7 +348,7 @@
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({});
+ request_params_.set_dlc_apps_params({});
fake_system_state_.set_request_params(&request_params_);
fake_system_state_.set_prefs(&fake_prefs_);
@@ -367,8 +368,8 @@
};
}
- // This function uses the paramets in |tuc_params_| to do an update check. It
- // will fill out |post_str| with the result data and |response| with
+ // This function uses the parameters in |tuc_params_| to do an update check.
+ // It will fill out |post_str| with the result data and |response| with
// |OmahaResponse|. Returns true iff an output response was obtained from the
// |OmahaRequestAction|. If |fail_http_response_code| is non-negative, the
// transfer will fail with that code. |ping_only| is passed through to the
@@ -405,6 +406,12 @@
bool expected_allow_p2p_for_sharing,
const string& expected_p2p_url);
+ // Helper function used to test the Ping request.
+ // Create the test directory and setup the Omaha response.
+ void SetUpStorePingReply(const string& dlc_id,
+ base::FilePath* metadata_path_dlc,
+ base::ScopedTempDir* tempdir);
+
FakeSystemState fake_system_state_;
FakeUpdateResponse fake_update_response_;
// Used by all tests.
@@ -2660,15 +2667,15 @@
TEST_F(OmahaRequestActionTest, InstallTest) {
request_params_.set_is_install(true);
- request_params_.set_dlc_module_ids({"dlc_no_0", "dlc_no_1"});
+ request_params_.set_dlc_apps_params(
+ {{request_params_.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}},
+ {request_params_.GetDlcAppId("dlc_no_1"), {.name = "dlc_no_1"}}});
tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
ASSERT_TRUE(TestUpdateCheck());
- 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 + "\""));
+ for (const auto& it : request_params_.dlc_apps_params()) {
+ EXPECT_NE(string::npos, post_str.find("appid=\"" + it.first + "\""));
}
EXPECT_NE(string::npos,
post_str.find("appid=\"" + fake_update_response_.app_id + "\""));
@@ -2680,14 +2687,16 @@
updatecheck_count++;
pos++;
}
- EXPECT_EQ(request_params_.dlc_module_ids().size(), updatecheck_count);
+ EXPECT_EQ(request_params_.dlc_apps_params().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_dlc_apps_params(
+ {{request_params_.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}},
+ {request_params_.GetDlcAppId("dlc_no_1"), {.name = "dlc_no_1"}}});
request_params_.set_app_id(fake_update_response_.app_id_skip_updatecheck);
tuc_params_.http_response = fake_update_response_.GetUpdateResponse();
@@ -2839,4 +2848,106 @@
EXPECT_EQ(kEolDateInvalid, StringToEolDate(eol_date));
}
+void OmahaRequestActionTest::SetUpStorePingReply(
+ const string& dlc_id,
+ base::FilePath* metadata_path_dlc,
+ base::ScopedTempDir* tempdir) {
+ // Create a uniquely named test directory.
+ ASSERT_TRUE(tempdir->CreateUniqueTempDir());
+ request_params_.set_root(tempdir->GetPath().value());
+ *metadata_path_dlc =
+ base::FilePath(request_params_.dlc_prefs_root()).Append(dlc_id);
+ ASSERT_TRUE(base::CreateDirectory(*metadata_path_dlc));
+
+ tuc_params_.http_response =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
+ "protocol=\"3.0\"><daystart elapsed_days=\"4763\" "
+ "elapsed_seconds=\"36540\"/><app appid=\"test-app-id\" status=\"ok\">\""
+ "<updatecheck status=\"noupdate\"/></app><app appid=\"test-app-id_dlc0\" "
+ "status=\"ok\"><ping status=\"ok\"/><updatecheck status=\"noupdate\"/>"
+ "</app></response>";
+ tuc_params_.expected_check_result = metrics::CheckResult::kNoUpdateAvailable;
+ tuc_params_.expected_check_reaction = metrics::CheckReaction::kUnset;
+}
+
+TEST_F(OmahaRequestActionTest, StorePingReplyNoPing) {
+ string dlc_id = "dlc0";
+ base::FilePath metadata_path_dlc0;
+ base::ScopedTempDir tempdir;
+ SetUpStorePingReply(dlc_id, &metadata_path_dlc0, &tempdir);
+ int64_t temp_int;
+ Prefs prefs;
+ ASSERT_TRUE(prefs.Init(metadata_path_dlc0));
+
+ OmahaRequestParams::AppParams app_param = {.name = dlc_id};
+ request_params_.set_dlc_apps_params(
+ {{request_params_.GetDlcAppId(dlc_id), app_param}});
+
+ ASSERT_TRUE(TestUpdateCheck());
+ // If there was no ping, the metadata files shouldn't exist yet.
+ EXPECT_FALSE(prefs.GetInt64(kPrefsPingActive, &temp_int));
+ EXPECT_FALSE(prefs.GetInt64(kPrefsPingLastActive, &temp_int));
+ EXPECT_FALSE(prefs.GetInt64(kPrefsPingLastRollcall, &temp_int));
+}
+
+TEST_F(OmahaRequestActionTest, StorePingReplyActiveTest) {
+ string dlc_id = "dlc0";
+ base::FilePath metadata_path_dlc0;
+ base::ScopedTempDir tempdir;
+ SetUpStorePingReply(dlc_id, &metadata_path_dlc0, &tempdir);
+ int64_t temp_int;
+ Prefs prefs;
+ ASSERT_TRUE(prefs.Init(metadata_path_dlc0));
+ // Create Active value
+ prefs.SetInt64(kPrefsPingActive, 0);
+
+ OmahaRequestParams::AppParams app_param = {
+ .active_counting_type = OmahaRequestParams::kDateBased,
+ .name = dlc_id,
+ .ping_active = 1,
+ .send_ping = true};
+ request_params_.set_dlc_apps_params(
+ {{request_params_.GetDlcAppId(dlc_id), app_param}});
+
+ ASSERT_TRUE(TestUpdateCheck());
+ EXPECT_TRUE(prefs.GetInt64(kPrefsPingActive, &temp_int));
+ EXPECT_EQ(temp_int, kPingInactiveValue);
+ EXPECT_TRUE(prefs.GetInt64(kPrefsPingLastActive, &temp_int));
+ EXPECT_EQ(temp_int, 4763);
+ EXPECT_TRUE(prefs.GetInt64(kPrefsPingLastRollcall, &temp_int));
+ EXPECT_EQ(temp_int, 4763);
+}
+
+TEST_F(OmahaRequestActionTest, StorePingReplyInactiveTest) {
+ string dlc_id = "dlc0";
+ base::FilePath metadata_path_dlc0;
+ base::ScopedTempDir tempdir;
+ SetUpStorePingReply(dlc_id, &metadata_path_dlc0, &tempdir);
+ int64_t temp_int;
+ Prefs prefs;
+ ASSERT_TRUE(prefs.Init(metadata_path_dlc0));
+ // Create Active value
+ prefs.SetInt64(kPrefsPingActive, 0);
+
+ OmahaRequestParams::AppParams app_param = {
+ .active_counting_type = OmahaRequestParams::kDateBased,
+ .name = dlc_id,
+ .ping_active = 0,
+ .send_ping = true};
+ request_params_.set_dlc_apps_params(
+ {{request_params_.GetDlcAppId(dlc_id), app_param}});
+
+ // Set the previous active value to an older value than 4763.
+ prefs.SetInt64(kPrefsPingLastActive, 555);
+
+ ASSERT_TRUE(TestUpdateCheck());
+ ASSERT_TRUE(prefs.Init(metadata_path_dlc0));
+ EXPECT_TRUE(prefs.GetInt64(kPrefsPingActive, &temp_int));
+ EXPECT_EQ(temp_int, kPingInactiveValue);
+ EXPECT_TRUE(prefs.GetInt64(kPrefsPingLastActive, &temp_int));
+ EXPECT_EQ(temp_int, 555);
+ EXPECT_TRUE(prefs.GetInt64(kPrefsPingLastRollcall, &temp_int));
+ EXPECT_EQ(temp_int, 4763);
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_request_builder_xml.cc b/omaha_request_builder_xml.cc
index 8439b42..e2bf307 100644
--- a/omaha_request_builder_xml.cc
+++ b/omaha_request_builder_xml.cc
@@ -36,8 +36,11 @@
namespace chromeos_update_engine {
-const int kNeverPinged = -1;
const char kNoVersion[] = "0.0.0.0";
+const int kPingNeverPinged = -1;
+const int kPingUnknownValue = -2;
+const int kPingActiveValue = 1;
+const int kPingInactiveValue = 0;
bool XmlEncode(const string& input, string* output) {
if (std::find_if(input.begin(), input.end(), [](const char c) {
@@ -87,7 +90,7 @@
// |name| and value |ping_days| if |ping_days| has a value that needs
// to be sent, or an empty string otherwise.
auto GetPingAttribute = [](const char* name, int ping_days) -> string {
- if (ping_days > 0 || ping_days == kNeverPinged)
+ if (ping_days > 0 || ping_days == kPingNeverPinged)
return base::StringPrintf(" %s=\"%d\"", name, ping_days);
return "";
};
@@ -102,13 +105,45 @@
return "";
}
-string OmahaRequestBuilderXml::GetAppBody(bool skip_updatecheck) const {
+string OmahaRequestBuilderXml::GetPingDateBased(
+ const OmahaRequestParams::AppParams& app_params) const {
+ if (!app_params.send_ping)
+ return "";
+ string ping_active = "";
+ string ping_ad = "";
+ if (app_params.ping_active == kPingActiveValue) {
+ ping_active =
+ base::StringPrintf(" active=\"%" PRId64 "\"", app_params.ping_active);
+ ping_ad = base::StringPrintf(" ad=\"%" PRId64 "\"",
+ app_params.ping_date_last_active);
+ }
+
+ string ping_rd = base::StringPrintf(" rd=\"%" PRId64 "\"",
+ app_params.ping_date_last_rollcall);
+
+ return base::StringPrintf(" <ping%s%s%s></ping>\n",
+ ping_active.c_str(),
+ ping_ad.c_str(),
+ ping_rd.c_str());
+}
+
+string OmahaRequestBuilderXml::GetAppBody(const OmahaAppData& app_data) const {
string app_body;
if (event_ == nullptr) {
- if (include_ping_)
- app_body = GetPing();
+ if (app_data.app_params.send_ping) {
+ switch (app_data.app_params.active_counting_type) {
+ case OmahaRequestParams::kDayBased:
+ app_body = GetPing();
+ break;
+ case OmahaRequestParams::kDateBased:
+ app_body = GetPingDateBased(app_data.app_params);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
if (!ping_only_) {
- if (!skip_updatecheck) {
+ if (!app_data.skip_update) {
app_body += " <updatecheck";
if (!params_->target_version_prefix().empty()) {
app_body += base::StringPrintf(
@@ -211,7 +246,7 @@
}
string OmahaRequestBuilderXml::GetApp(const OmahaAppData& app_data) const {
- string app_body = GetAppBody(app_data.skip_update);
+ string app_body = GetAppBody(app_data);
string app_versions;
// If we are downgrading to a more stable channel and we are allowed to do
@@ -370,23 +405,28 @@
.product_components = params_->product_components(),
// Skips updatecheck for platform app in case of an install operation.
.skip_update = params_->is_install(),
- .is_dlc = false};
+ .is_dlc = false,
+
+ .app_params = {.active_counting_type = OmahaRequestParams::kDayBased,
+ .send_ping = include_ping_}};
app_xml += GetApp(product_app);
if (!params_->system_app_id().empty()) {
- OmahaAppData system_app = {.id = params_->system_app_id(),
- .version = params_->system_version(),
- .skip_update = false,
- .is_dlc = false};
+ OmahaAppData system_app = {
+ .id = params_->system_app_id(),
+ .version = params_->system_version(),
+ .skip_update = false,
+ .is_dlc = false,
+ .app_params = {.active_counting_type = OmahaRequestParams::kDayBased,
+ .send_ping = include_ping_}};
app_xml += GetApp(system_app);
}
- // 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()) {
+ for (const auto& it : params_->dlc_apps_params()) {
OmahaAppData dlc_module_app = {
- .id = params_->GetAppId() + "_" + dlc_module_id,
+ .id = it.first,
.version = params_->is_install() ? kNoVersion : params_->app_version(),
.skip_update = false,
- .is_dlc = true};
+ .is_dlc = true,
+ .app_params = it.second};
app_xml += GetApp(dlc_module_app);
}
return app_xml;
diff --git a/omaha_request_builder_xml.h b/omaha_request_builder_xml.h
index d7a81d3..50c708d 100644
--- a/omaha_request_builder_xml.h
+++ b/omaha_request_builder_xml.h
@@ -33,13 +33,17 @@
#include "update_engine/common/action.h"
#include "update_engine/common/http_fetcher.h"
+#include "update_engine/omaha_request_params.h"
#include "update_engine/omaha_response.h"
#include "update_engine/system_state.h"
namespace chromeos_update_engine {
-extern const int kNeverPinged;
extern const char kNoVersion[];
+extern const int kPingNeverPinged;
+extern const int kPingUnknownValue;
+extern const int kPingActiveValue;
+extern const int kPingInactiveValue;
// This struct encapsulates the Omaha event information. For a
// complete list of defined event types and results, see
@@ -87,6 +91,7 @@
std::string product_components;
bool skip_update;
bool is_dlc;
+ OmahaRequestParams::AppParams app_params;
};
// Encodes XML entities in a given string. Input must be ASCII-7 valid. If
@@ -158,9 +163,7 @@
// Returns an XML that goes into the body of the <app> element of the Omaha
// request based on the given parameters.
- // The skip_updatecheck argument if set to true will omit the emission of
- // the updatecheck xml tag in the body of the <app> element.
- std::string GetAppBody(bool skip_updatecheck) const;
+ std::string GetAppBody(const OmahaAppData& app_data) const;
// Returns the cohort* argument to include in the <app> tag for the passed
// |arg_name| and |prefs_key|, if any. The return value is suitable to
@@ -173,6 +176,11 @@
// sent, or an empty string otherwise.
std::string GetPing() const;
+ // Returns an XML ping element if any of the elapsed days need to be
+ // sent, or an empty string otherwise.
+ std::string GetPingDateBased(
+ const OmahaRequestParams::AppParams& app_params) const;
+
const OmahaEvent* event_;
OmahaRequestParams* params_;
bool ping_only_;
diff --git a/omaha_request_builder_xml_unittest.cc b/omaha_request_builder_xml_unittest.cc
index 8cf7473..3cf5cc0 100644
--- a/omaha_request_builder_xml_unittest.cc
+++ b/omaha_request_builder_xml_unittest.cc
@@ -198,7 +198,9 @@
TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlPlatformUpdateWithDlcsTest) {
OmahaRequestParams omaha_request_params{&fake_system_state_};
- omaha_request_params.set_dlc_module_ids({"dlc_1", "dlc_2"});
+ omaha_request_params.set_dlc_apps_params(
+ {{omaha_request_params.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}},
+ {omaha_request_params.GetDlcAppId("dlc_no_1"), {.name = "dlc_no_1"}}});
OmahaRequestBuilderXml omaha_request{nullptr,
&omaha_request_params,
false,
@@ -215,8 +217,10 @@
TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcInstallationTest) {
OmahaRequestParams omaha_request_params{&fake_system_state_};
- const vector<string> dlcs = {"dlc_1", "dlc_2"};
- omaha_request_params.set_dlc_module_ids(dlcs);
+ const std::map<std::string, OmahaRequestParams::AppParams> dlcs = {
+ {omaha_request_params.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}},
+ {omaha_request_params.GetDlcAppId("dlc_no_1"), {.name = "dlc_no_1"}}};
+ omaha_request_params.set_dlc_apps_params(dlcs);
omaha_request_params.set_is_install(true);
OmahaRequestBuilderXml omaha_request{nullptr,
&omaha_request_params,
@@ -250,4 +254,69 @@
}
}
+TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcNoPing) {
+ OmahaRequestParams omaha_request_params{&fake_system_state_};
+ omaha_request_params.set_dlc_apps_params(
+ {{omaha_request_params.GetDlcAppId("dlc_no_0"), {.name = "dlc_no_0"}}});
+ OmahaRequestBuilderXml omaha_request{nullptr,
+ &omaha_request_params,
+ false,
+ false,
+ 0,
+ 0,
+ 0,
+ fake_system_state_.prefs(),
+ ""};
+ const string request_xml = omaha_request.GetRequest();
+ EXPECT_EQ(0, CountSubstringInString(request_xml, "<ping")) << request_xml;
+}
+
+TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcPingRollCallNoActive) {
+ OmahaRequestParams omaha_request_params{&fake_system_state_};
+ omaha_request_params.set_dlc_apps_params(
+ {{omaha_request_params.GetDlcAppId("dlc_no_0"),
+ {.active_counting_type = OmahaRequestParams::kDateBased,
+ .name = "dlc_no_0",
+ .ping_date_last_active = 25,
+ .ping_date_last_rollcall = 36,
+ .send_ping = true}}});
+ OmahaRequestBuilderXml omaha_request{nullptr,
+ &omaha_request_params,
+ false,
+ false,
+ 0,
+ 0,
+ 0,
+ fake_system_state_.prefs(),
+ ""};
+ const string request_xml = omaha_request.GetRequest();
+ EXPECT_EQ(1, CountSubstringInString(request_xml, "<ping rd=\"36\""))
+ << request_xml;
+}
+
+TEST_F(OmahaRequestBuilderXmlTest, GetRequestXmlDlcPingRollCallAndActive) {
+ OmahaRequestParams omaha_request_params{&fake_system_state_};
+ omaha_request_params.set_dlc_apps_params(
+ {{omaha_request_params.GetDlcAppId("dlc_no_0"),
+ {.active_counting_type = OmahaRequestParams::kDateBased,
+ .name = "dlc_no_0",
+ .ping_active = 1,
+ .ping_date_last_active = 25,
+ .ping_date_last_rollcall = 36,
+ .send_ping = true}}});
+ OmahaRequestBuilderXml omaha_request{nullptr,
+ &omaha_request_params,
+ false,
+ false,
+ 0,
+ 0,
+ 0,
+ fake_system_state_.prefs(),
+ ""};
+ const string request_xml = omaha_request.GetRequest();
+ EXPECT_EQ(1,
+ CountSubstringInString(request_xml,
+ "<ping active=\"1\" ad=\"25\" rd=\"36\""))
+ << request_xml;
+}
} // namespace chromeos_update_engine
diff --git a/omaha_request_params.cc b/omaha_request_params.cc
index 70867a3..e6d96a4 100644
--- a/omaha_request_params.cc
+++ b/omaha_request_params.cc
@@ -124,7 +124,7 @@
// Set the interactive flag accordingly.
interactive_ = in_interactive;
- dlc_module_ids_.clear();
+ dlc_apps_params_.clear();
// Set false so it will do update by default.
is_install_ = false;
return true;
@@ -215,6 +215,7 @@
void OmahaRequestParams::set_root(const string& root) {
root_ = root;
test::SetImagePropertiesRootPrefix(root_.c_str());
+ dlc_prefs_root_ = root + kDlcMetadataRootpath;
}
int OmahaRequestParams::GetChannelIndex(const string& channel) const {
@@ -248,4 +249,10 @@
: image_props_.product_id;
}
+string OmahaRequestParams::GetDlcAppId(std::string dlc_id) const {
+ // Create APP ID according to |dlc_id| (sticking the current AppID to the
+ // DLC module ID with an underscode).
+ return GetAppId() + "_" + dlc_id;
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_request_params.h b/omaha_request_params.h
index 6b579ec..b984002 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -19,6 +19,7 @@
#include <stdint.h>
+#include <map>
#include <string>
#include <vector>
@@ -26,6 +27,7 @@
#include <base/time/time.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
+#include "update_engine/common/constants.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/image_properties.h"
@@ -56,10 +58,26 @@
update_check_count_wait_enabled_(false),
min_update_checks_needed_(kDefaultMinUpdateChecks),
max_update_checks_allowed_(kDefaultMaxUpdateChecks),
+ dlc_prefs_root_(kDlcMetadataRootpath),
is_install_(false) {}
virtual ~OmahaRequestParams();
+ enum ActiveCountingType {
+ kDayBased = 0,
+ kDateBased,
+ };
+
+ struct AppParams {
+ ActiveCountingType active_counting_type;
+ // |name| is only used for DLCs to store the DLC ID.
+ std::string name;
+ int64_t ping_active;
+ int64_t ping_date_last_active;
+ int64_t ping_date_last_rollcall;
+ bool send_ping;
+ };
+
// Setters and getters for the various properties.
inline std::string os_platform() const { return os_platform_; }
inline std::string os_version() const { return os_version_; }
@@ -184,12 +202,12 @@
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 void set_dlc_apps_params(
+ const std::map<std::string, AppParams>& dlc_apps_params) {
+ dlc_apps_params_ = dlc_apps_params;
}
- inline std::vector<std::string> dlc_module_ids() const {
- return dlc_module_ids_;
+ inline const std::map<std::string, AppParams>& dlc_apps_params() const {
+ return dlc_apps_params_;
}
inline void set_is_install(bool is_install) { is_install_ = is_install; }
inline bool is_install() const { return is_install_; }
@@ -201,10 +219,16 @@
return autoupdate_token_;
}
- // Returns the app id corresponding to the current value of the
+ inline std::string dlc_prefs_root() const { return dlc_prefs_root_; }
+
+ // Returns the App ID corresponding to the current value of the
// download channel.
virtual std::string GetAppId() const;
+ // Returns the DLC app ID corresponding to the current value of the
+ // download channel.
+ virtual std::string GetDlcAppId(std::string dlc_id) const;
+
// Suggested defaults
static const char kOsVersion[];
static const int64_t kDefaultMinUpdateChecks = 0;
@@ -377,8 +401,11 @@
// 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_;
+ // The metadata/prefs root path for DLCs.
+ std::string dlc_prefs_root_;
+
+ // A list of DLC modules to install.
+ std::map<std::string, AppParams> dlc_apps_params_;
// 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
diff --git a/update_attempter.cc b/update_attempter.cc
index f5e2037..8e32091 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <algorithm>
+#include <map>
#include <memory>
#include <string>
#include <utility>
@@ -29,6 +30,7 @@
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/rand_util.h>
+#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
@@ -46,6 +48,7 @@
#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.h"
#include "update_engine/common/prefs_interface.h"
#include "update_engine/common/subprocess.h"
#include "update_engine/common/utils.h"
@@ -70,6 +73,7 @@
using base::Bind;
using base::Callback;
+using base::FilePath;
using base::Time;
using base::TimeDelta;
using base::TimeTicks;
@@ -427,14 +431,13 @@
omaha_request_params_->UpdateDownloadChannel();
}
- // Set the |dlc_module_ids_| only for an update. This is required to get the
- // currently installed DLC(s).
- if (!is_install_ &&
- !system_state_->dlcservice()->GetInstalled(&dlc_module_ids_)) {
- LOG(INFO) << "Failed to retrieve DLC module IDs from dlcservice. Check the "
- "state of dlcservice, will not update DLC modules.";
- }
- omaha_request_params_->set_dlc_module_ids(dlc_module_ids_);
+ // The function |CalculateDlcParams| makes use of the function |GetAppId| from
+ // |OmahaRequestParams|, so to ensure that the return from |GetAppId|
+ // doesn't change, no changes to the values |download_channel_|,
+ // |image_props_.product_id| and |image_props_.canary_product_id| from
+ // |omaha_request_params_| shall be made below this line.
+ CalculateDlcParams();
+
omaha_request_params_->set_is_install(is_install_);
// Set Quick Fix Build token if policy is set and the device is enterprise
@@ -653,6 +656,66 @@
}
}
+int64_t UpdateAttempter::GetPingMetadata(
+ const PrefsInterface& prefs, const std::string& metadata_name) const {
+ // The first time a ping is sent, the metadata files containing the values
+ // sent back by the server still don't exist. A value of -1 is used to
+ // indicate this.
+ if (!prefs.Exists(metadata_name))
+ return kPingNeverPinged;
+
+ int64_t value;
+ if (prefs.GetInt64(metadata_name, &value))
+ return value;
+
+ // Return -2 when the file exists and there is a problem reading from it, or
+ // the value cannot be converted to an integer.
+ return kPingUnknownValue;
+}
+
+void UpdateAttempter::CalculateDlcParams() {
+ // Set the |dlc_module_ids_| only for an update. This is required to get the
+ // currently installed DLC(s).
+ if (!is_install_ &&
+ !system_state_->dlcservice()->GetInstalled(&dlc_module_ids_)) {
+ LOG(INFO) << "Failed to retrieve DLC module IDs from dlcservice. Check the "
+ "state of dlcservice, will not update DLC modules.";
+ }
+ std::map<std::string, OmahaRequestParams::AppParams> dlc_apps_params;
+ for (auto dlc_id : dlc_module_ids_) {
+ OmahaRequestParams::AppParams dlc_params{
+ .active_counting_type = OmahaRequestParams::kDateBased,
+ .name = dlc_id,
+ .send_ping = false};
+ // Only send the ping when the request is to update DLCs. When installing
+ // DLCs, we don't want to send the ping yet, since the DLCs might fail to
+ // install or might not really be active yet.
+ if (!is_install_) {
+ base::FilePath metadata_path =
+ base::FilePath(omaha_request_params_->dlc_prefs_root())
+ .Append(dlc_id);
+ Prefs prefs;
+ if (!prefs.Init(metadata_path)) {
+ LOG(ERROR) << "Failed to initialize the preferences path:"
+ << metadata_path.value() << ".";
+ } else {
+ dlc_params.ping_active = kPingActiveValue;
+ if (!prefs.GetInt64(kPrefsPingActive, &dlc_params.ping_active) ||
+ dlc_params.ping_active != kPingActiveValue) {
+ dlc_params.ping_active = kPingInactiveValue;
+ }
+ dlc_params.ping_date_last_active =
+ GetPingMetadata(prefs, kPrefsPingLastActive);
+ dlc_params.ping_date_last_rollcall =
+ GetPingMetadata(prefs, kPrefsPingLastRollcall);
+ dlc_params.send_ping = true;
+ }
+ }
+ dlc_apps_params[omaha_request_params_->GetDlcAppId(dlc_id)] = dlc_params;
+ }
+ omaha_request_params_->set_dlc_apps_params(dlc_apps_params);
+}
+
void UpdateAttempter::BuildUpdateActions(bool interactive) {
CHECK(!processor_->IsRunning());
processor_->set_delegate(this);
diff --git a/update_attempter.h b/update_attempter.h
index 51b672d..91e072a 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -249,6 +249,10 @@
FRIEND_TEST(UpdateAttempterTest, ActionCompletedOmahaRequestTest);
FRIEND_TEST(UpdateAttempterTest, BootTimeInUpdateMarkerFile);
FRIEND_TEST(UpdateAttempterTest, BroadcastCompleteDownloadTest);
+ FRIEND_TEST(UpdateAttempterTest, CalculateDlcParamsInstallTest);
+ FRIEND_TEST(UpdateAttempterTest, CalculateDlcParamsNoPrefFilesTest);
+ FRIEND_TEST(UpdateAttempterTest, CalculateDlcParamsNonParseableValuesTest);
+ FRIEND_TEST(UpdateAttempterTest, CalculateDlcParamsValidValuesTest);
FRIEND_TEST(UpdateAttempterTest, ChangeToDownloadingOnReceivedBytesTest);
FRIEND_TEST(UpdateAttempterTest, CheckForInstallNotIdleFails);
FRIEND_TEST(UpdateAttempterTest, CheckForUpdateAUDlcTest);
@@ -430,6 +434,18 @@
// Resets interactivity and forced update flags.
void ResetInteractivityFlags();
+ // Get the integer values from the metadata directory set in |prefs| for
+ // |kPrefsPingLastActive| or |kPrefsPingLastRollcall|.
+ // The value is equal to -2 when the value cannot be read or is not numeric.
+ // The value is equal to -1 the first time it is being sent, which is
+ // when the metadata file doesn't exist.
+ int64_t GetPingMetadata(const PrefsInterface& prefs,
+ const std::string& metadata_name) const;
+
+ // Calculates the update parameters for DLCs. Sets the |dlc_modules_|
+ // parameter on the |omaha_request_params_| object.
+ void CalculateDlcParams();
+
// 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.
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 4aff897..d468f56 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -2278,4 +2278,127 @@
EXPECT_EQ(eol_date, status.eol_date);
}
+TEST_F(UpdateAttempterTest, CalculateDlcParamsInstallTest) {
+ // Create a uniquely named test directory.
+ base::ScopedTempDir tempdir;
+ ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+ fake_system_state_.request_params()->set_root(tempdir.GetPath().value());
+ string dlc_id = "dlc0";
+ base::FilePath metadata_path_dlc0 =
+ base::FilePath(fake_system_state_.request_params()->dlc_prefs_root())
+ .Append(dlc_id);
+
+ ASSERT_TRUE(base::CreateDirectory(metadata_path_dlc0));
+ attempter_.is_install_ = true;
+ attempter_.dlc_module_ids_ = {dlc_id};
+ attempter_.CalculateDlcParams();
+
+ OmahaRequestParams* params = fake_system_state_.request_params();
+ EXPECT_EQ(1, params->dlc_apps_params().count(params->GetDlcAppId(dlc_id)));
+ OmahaRequestParams::AppParams dlc_app_params =
+ params->dlc_apps_params().at(params->GetDlcAppId(dlc_id));
+ EXPECT_STREQ(dlc_id.c_str(), dlc_app_params.name.c_str());
+ EXPECT_EQ(false, dlc_app_params.send_ping);
+ // When the DLC gets installed, a ping is not sent, therefore we don't store
+ // the values sent by Omaha.
+ EXPECT_FALSE(
+ base::PathExists(metadata_path_dlc0.Append(kPrefsPingLastActive)));
+ EXPECT_FALSE(
+ base::PathExists(metadata_path_dlc0.Append(kPrefsPingLastRollcall)));
+}
+
+TEST_F(UpdateAttempterTest, CalculateDlcParamsNoPrefFilesTest) {
+ // Create a uniquely named test directory.
+ base::ScopedTempDir tempdir;
+ ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+ fake_system_state_.request_params()->set_root(tempdir.GetPath().value());
+ string dlc_id = "dlc0";
+ base::FilePath metadata_path_dlc0 =
+ base::FilePath(fake_system_state_.request_params()->dlc_prefs_root())
+ .Append(dlc_id);
+ ASSERT_TRUE(base::CreateDirectory(metadata_path_dlc0));
+ EXPECT_CALL(mock_dlcservice_, GetInstalled(_))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(std::vector<string>({dlc_id})), Return(true)));
+
+ attempter_.is_install_ = false;
+ attempter_.CalculateDlcParams();
+
+ OmahaRequestParams* params = fake_system_state_.request_params();
+ EXPECT_EQ(1, params->dlc_apps_params().count(params->GetDlcAppId(dlc_id)));
+ OmahaRequestParams::AppParams dlc_app_params =
+ params->dlc_apps_params().at(params->GetDlcAppId(dlc_id));
+ EXPECT_STREQ(dlc_id.c_str(), dlc_app_params.name.c_str());
+
+ EXPECT_EQ(true, dlc_app_params.send_ping);
+ EXPECT_EQ(0, dlc_app_params.ping_active);
+ EXPECT_EQ(-1, dlc_app_params.ping_date_last_active);
+ EXPECT_EQ(-1, dlc_app_params.ping_date_last_rollcall);
+}
+
+TEST_F(UpdateAttempterTest, CalculateDlcParamsNonParseableValuesTest) {
+ // Create a uniquely named test directory.
+ base::ScopedTempDir tempdir;
+ ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+ fake_system_state_.request_params()->set_root(tempdir.GetPath().value());
+ string dlc_id = "dlc0";
+ base::FilePath metadata_path_dlc0 =
+ base::FilePath(fake_system_state_.request_params()->dlc_prefs_root())
+ .Append(dlc_id);
+ ASSERT_TRUE(base::CreateDirectory(metadata_path_dlc0));
+ EXPECT_CALL(mock_dlcservice_, GetInstalled(_))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(std::vector<string>({dlc_id})), Return(true)));
+
+ // Write non numeric values in the metadata files.
+ base::WriteFile(metadata_path_dlc0.Append(kPrefsPingActive), "z2yz", 4);
+ base::WriteFile(metadata_path_dlc0.Append(kPrefsPingLastActive), "z2yz", 4);
+ base::WriteFile(metadata_path_dlc0.Append(kPrefsPingLastRollcall), "z2yz", 4);
+ attempter_.is_install_ = false;
+ attempter_.CalculateDlcParams();
+
+ OmahaRequestParams* params = fake_system_state_.request_params();
+ EXPECT_EQ(1, params->dlc_apps_params().count(params->GetDlcAppId(dlc_id)));
+ OmahaRequestParams::AppParams dlc_app_params =
+ params->dlc_apps_params().at(params->GetDlcAppId(dlc_id));
+ EXPECT_STREQ(dlc_id.c_str(), dlc_app_params.name.c_str());
+
+ EXPECT_EQ(true, dlc_app_params.send_ping);
+ EXPECT_EQ(0, dlc_app_params.ping_active);
+ EXPECT_EQ(-2, dlc_app_params.ping_date_last_active);
+ EXPECT_EQ(-2, dlc_app_params.ping_date_last_rollcall);
+}
+
+TEST_F(UpdateAttempterTest, CalculateDlcParamsValidValuesTest) {
+ // Create a uniquely named test directory.
+ base::ScopedTempDir tempdir;
+ ASSERT_TRUE(tempdir.CreateUniqueTempDir());
+ fake_system_state_.request_params()->set_root(tempdir.GetPath().value());
+ string dlc_id = "dlc0";
+ base::FilePath metadata_path_dlc0 =
+ base::FilePath(fake_system_state_.request_params()->dlc_prefs_root())
+ .Append(dlc_id);
+ ASSERT_TRUE(base::CreateDirectory(metadata_path_dlc0));
+ EXPECT_CALL(mock_dlcservice_, GetInstalled(_))
+ .WillOnce(
+ DoAll(SetArgPointee<0>(std::vector<string>({dlc_id})), Return(true)));
+
+ // Write numeric values in the metadata files.
+ base::WriteFile(metadata_path_dlc0.Append(kPrefsPingActive), "1", 1);
+ base::WriteFile(metadata_path_dlc0.Append(kPrefsPingLastActive), "78", 2);
+ base::WriteFile(metadata_path_dlc0.Append(kPrefsPingLastRollcall), "99", 2);
+ attempter_.is_install_ = false;
+ attempter_.CalculateDlcParams();
+
+ OmahaRequestParams* params = fake_system_state_.request_params();
+ EXPECT_EQ(1, params->dlc_apps_params().count(params->GetDlcAppId(dlc_id)));
+ OmahaRequestParams::AppParams dlc_app_params =
+ params->dlc_apps_params().at(params->GetDlcAppId(dlc_id));
+ EXPECT_STREQ(dlc_id.c_str(), dlc_app_params.name.c_str());
+
+ EXPECT_EQ(true, dlc_app_params.send_ping);
+ EXPECT_EQ(1, dlc_app_params.ping_active);
+ EXPECT_EQ(78, dlc_app_params.ping_date_last_active);
+ EXPECT_EQ(99, dlc_app_params.ping_date_last_rollcall);
+}
} // namespace chromeos_update_engine