odrefresh: add support for lastUpdateMillis as part of version check
This is to support samegrade updates for module evaluation.
(cherry picked from commit 79f874d287cb314dcf3fb8a78122d4296e91bd42)
Bug: 192647837
Test: atest art_standalone_odrefresh_tests
Test: atest odsign_e2e_tests
Merged-In: Ied43ebdcc4b2ec57e337e709970fab948cf5f992
Change-Id: I10d8e63cefe2e010f0856e0be71a5afe73b6f76f
diff --git a/odrefresh/CacheInfo.xsd b/odrefresh/CacheInfo.xsd
index a0c00af..b58453a 100644
--- a/odrefresh/CacheInfo.xsd
+++ b/odrefresh/CacheInfo.xsd
@@ -38,6 +38,8 @@
<xs:attribute name="versionCode" type="xs:long" use="required" />
<!-- Module versionName for the active ART APEX from `/apex/apex-info-list.xml`. -->
<xs:attribute name="versionName" type="xs:string" use="required" />
+ <!-- Module lastUpdateMillis for the active ART APEX from `/apex/apex-info-list.xml`. -->
+ <xs:attribute name="lastUpdateMillis" type="xs:long" use="required" />
</xs:complexType>
<!-- Components of the `DEX2OATBOOTCLASSPATH`. -->
diff --git a/odrefresh/odr_compilation_log.cc b/odrefresh/odr_compilation_log.cc
index 55432f4..37804a2 100644
--- a/odrefresh/odr_compilation_log.cc
+++ b/odrefresh/odr_compilation_log.cc
@@ -41,7 +41,9 @@
auto saved_exceptions = is.exceptions();
is.exceptions(std::ios_base::iostate {});
+ // Write log entry. NB update OdrCompilationLog::kLogVersion if changing the format here.
is >> entry.apex_version >> std::ws;
+ is >> entry.last_update_millis >> std::ws;
is >> entry.trigger >> std::ws;
is >> entry.when >> std::ws;
is >> entry.exit_code >> std::ws;
@@ -59,6 +61,7 @@
os.exceptions(std::ios_base::iostate {});
os << entry.apex_version << kSpace;
+ os << entry.last_update_millis << kSpace;
os << entry.trigger << kSpace;
os << entry.when << kSpace;
os << entry.exit_code << std::endl;
@@ -69,8 +72,8 @@
}
bool operator==(const OdrCompilationLogEntry& lhs, const OdrCompilationLogEntry& rhs) {
- return lhs.apex_version == rhs.apex_version && lhs.trigger == rhs.trigger &&
- lhs.when == rhs.when && lhs.exit_code == rhs.exit_code;
+ return lhs.apex_version == rhs.apex_version && lhs.last_update_millis == rhs.last_update_millis &&
+ lhs.trigger == rhs.trigger && lhs.when == rhs.when && lhs.exit_code == rhs.exit_code;
}
bool operator!=(const OdrCompilationLogEntry& lhs, const OdrCompilationLogEntry& rhs) {
@@ -98,6 +101,12 @@
return false;
}
+ std::string log_version;
+ ifs >> log_version >> std::ws;
+ if (log_version != kLogVersion) {
+ return false;
+ }
+
while (!ifs.eof()) {
OdrCompilationLogEntry entry;
ifs >> entry;
@@ -117,6 +126,7 @@
return false;
}
+ ofs << kLogVersion << std::endl;
for (const auto& entry : entries_) {
ofs << entry;
if (ofs.fail()) {
@@ -148,23 +158,29 @@
}
void OdrCompilationLog::Log(int64_t apex_version,
+ int64_t last_update_millis,
OdrMetrics::Trigger trigger,
ExitCode compilation_result) {
time_t now;
time(&now);
- Log(apex_version, trigger, now, compilation_result);
+ Log(apex_version, last_update_millis, trigger, now, compilation_result);
}
void OdrCompilationLog::Log(int64_t apex_version,
+ int64_t last_update_millis,
OdrMetrics::Trigger trigger,
time_t when,
ExitCode compilation_result) {
- entries_.push_back(OdrCompilationLogEntry{
- apex_version, static_cast<int32_t>(trigger), when, static_cast<int32_t>(compilation_result)});
+ entries_.push_back(OdrCompilationLogEntry{apex_version,
+ last_update_millis,
+ static_cast<int32_t>(trigger),
+ when,
+ static_cast<int32_t>(compilation_result)});
Truncate();
}
bool OdrCompilationLog::ShouldAttemptCompile(int64_t apex_version,
+ int64_t last_update_millis,
OdrMetrics::Trigger trigger,
time_t now) const {
if (entries_.size() == 0) {
@@ -173,7 +189,12 @@
}
if (apex_version != entries_.back().apex_version) {
- // There is a new ART APEX, we should use compile right away.
+ // There is a new ART APEX, we should compile right away.
+ return true;
+ }
+
+ if (last_update_millis != entries_.back().last_update_millis) {
+ // There is a samegrade ART APEX update, we should compile right away.
return true;
}
diff --git a/odrefresh/odr_compilation_log.h b/odrefresh/odr_compilation_log.h
index 6f13c97..894079c 100644
--- a/odrefresh/odr_compilation_log.h
+++ b/odrefresh/odr_compilation_log.h
@@ -32,6 +32,7 @@
// OdrCompilationLogEntry represents the result of a compilation attempt by odrefresh.
struct OdrCompilationLogEntry {
int64_t apex_version;
+ int64_t last_update_millis;
int32_t trigger;
time_t when;
int32_t exit_code;
@@ -53,6 +54,11 @@
// directory is only used by odrefresh whereas the ART apexdata directory is also used by odsign
// and others which may lead to the deletion (or rollback) of the log file.
static constexpr const char* kCompilationLogFile = "/data/misc/odrefresh/compilation-log.txt";
+
+ // Version string that appears on the first line of the compilation log.
+ static constexpr const char kLogVersion[] = "CompilationLog/1.0";
+
+ // Number of log entries in the compilation log.
static constexpr const size_t kMaxLoggedEntries = 4;
explicit OdrCompilationLog(const char* compilation_log_path = kCompilationLogFile);
@@ -60,6 +66,7 @@
// Applies policy to compilation log to determine whether to recompile.
bool ShouldAttemptCompile(int64_t apex_version,
+ int64_t last_update_millis,
OdrMetrics::Trigger trigger,
time_t now = 0) const;
@@ -69,9 +76,13 @@
// Returns the entry at position `index` or nullptr if `index` is out of bounds.
const OdrCompilationLogEntry* Peek(size_t index) const;
- void Log(int64_t apex_version, OdrMetrics::Trigger trigger, ExitCode compilation_result);
+ void Log(int64_t apex_version,
+ int64_t last_update_millis,
+ OdrMetrics::Trigger trigger,
+ ExitCode compilation_result);
void Log(int64_t apex_version,
+ int64_t last_update_millis,
OdrMetrics::Trigger trigger,
time_t when,
ExitCode compilation_result);
diff --git a/odrefresh/odr_compilation_log_test.cc b/odrefresh/odr_compilation_log_test.cc
index c5c9555..d99b4d9 100644
--- a/odrefresh/odr_compilation_log_test.cc
+++ b/odrefresh/odr_compilation_log_test.cc
@@ -28,8 +28,8 @@
#include <string>
#include <vector>
+#include "android-base/file.h"
#include "base/common_art_test.h"
-
#include "odrefresh/odrefresh.h"
#include "odr_metrics.h"
@@ -41,28 +41,31 @@
class OdrCompilationLogTest : public CommonArtTest {};
TEST(OdrCompilationLogEntry, Equality) {
- OdrCompilationLogEntry a{1, 2, 3, 4};
+ OdrCompilationLogEntry a{1, 2, 3, 4, 5};
- ASSERT_EQ(a, (OdrCompilationLogEntry{1, 2, 3, 4}));
- ASSERT_NE(a, (OdrCompilationLogEntry{9, 2, 3, 4}));
- ASSERT_NE(a, (OdrCompilationLogEntry{1, 9, 3, 4}));
- ASSERT_NE(a, (OdrCompilationLogEntry{1, 2, 9, 4}));
- ASSERT_NE(a, (OdrCompilationLogEntry{2, 2, 3, 9}));
+ ASSERT_EQ(a, (OdrCompilationLogEntry{1, 2, 3, 4, 5}));
+ ASSERT_NE(a, (OdrCompilationLogEntry{9, 2, 3, 4, 5}));
+ ASSERT_NE(a, (OdrCompilationLogEntry{1, 9, 3, 4, 5}));
+ ASSERT_NE(a, (OdrCompilationLogEntry{1, 2, 9, 4, 5}));
+ ASSERT_NE(a, (OdrCompilationLogEntry{2, 2, 3, 9, 5}));
+ ASSERT_NE(a, (OdrCompilationLogEntry{2, 2, 3, 5, 9}));
}
TEST(OdrCompilationLogEntry, InputOutput) {
const OdrCompilationLogEntry entries[] = {
- {1, 2, 3, 4},
+ {1, 2, 3, 4, 5},
{std::numeric_limits<int64_t>::min(),
+ std::numeric_limits<int64_t>::min(),
std::numeric_limits<int32_t>::min(),
std::numeric_limits<time_t>::min(),
std::numeric_limits<int32_t>::min()},
{std::numeric_limits<int64_t>::max(),
+ std::numeric_limits<int64_t>::max(),
std::numeric_limits<int32_t>::max(),
std::numeric_limits<time_t>::max(),
std::numeric_limits<int32_t>::max()},
- {0, 0, 0, 0},
- {0x7fedcba9'87654321, 0x12345678, 0x2346789, 0x76543210}
+ {0, 0, 0, 0, 0},
+ {0x7fedcba9'87654321, 0x5a5a5a5a'5a5a5a5a, 0x12345678, 0x2346789, 0x76543210}
};
for (const auto& entry : entries) {
std::stringstream ss;
@@ -86,12 +89,12 @@
TEST(OdrCompilationLogEntry, ReadMultiple) {
std::stringstream ss;
- ss << "1 2 3 4\n5 6 7 8\n";
+ ss << "0 1 2 3 4\n5 6 7 8 9\n";
OdrCompilationLogEntry entry0, entry1;
ss >> entry0 >> entry1;
- ASSERT_EQ(entry0, (OdrCompilationLogEntry{1, 2, 3, 4}));
- ASSERT_EQ(entry1, (OdrCompilationLogEntry{5, 6, 7, 8}));
+ ASSERT_EQ(entry0, (OdrCompilationLogEntry{0, 1, 2, 3, 4}));
+ ASSERT_EQ(entry1, (OdrCompilationLogEntry{5, 6, 7, 8, 9}));
ASSERT_FALSE(ss.fail());
ASSERT_FALSE(ss.bad());
@@ -100,14 +103,24 @@
TEST(OdrCompilationLog, ShouldAttemptCompile) {
OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
- ASSERT_TRUE(ocl.ShouldAttemptCompile(1, OdrMetrics::Trigger::kMissingArtifacts, 0));
+ ASSERT_TRUE(ocl.ShouldAttemptCompile(
+ /*apex_version=*/1, /*last_update_millis=*/762, OdrMetrics::Trigger::kMissingArtifacts, 0));
ocl.Log(
- /*apex_version=*/1, OdrMetrics::Trigger::kApexVersionMismatch, ExitCode::kCompilationSuccess);
- ASSERT_TRUE(ocl.ShouldAttemptCompile(2, OdrMetrics::Trigger::kApexVersionMismatch));
- ASSERT_FALSE(ocl.ShouldAttemptCompile(1, OdrMetrics::Trigger::kApexVersionMismatch));
- ASSERT_TRUE(ocl.ShouldAttemptCompile(1, OdrMetrics::Trigger::kDexFilesChanged));
- ASSERT_FALSE(ocl.ShouldAttemptCompile(1, OdrMetrics::Trigger::kUnknown));
+ /*apex_version=*/1,
+ /*last_update_millis=*/762,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ ExitCode::kCompilationSuccess);
+ ASSERT_TRUE(ocl.ShouldAttemptCompile(
+ /*apex_version=*/2, /*last_update_millis=*/762, OdrMetrics::Trigger::kApexVersionMismatch));
+ ASSERT_TRUE(ocl.ShouldAttemptCompile(
+ /*apex_version=*/1, /*last_update_millis=*/10000, OdrMetrics::Trigger::kApexVersionMismatch));
+ ASSERT_FALSE(ocl.ShouldAttemptCompile(
+ /*apex_version=*/1, /*last_update_millis=*/762, OdrMetrics::Trigger::kApexVersionMismatch));
+ ASSERT_TRUE(ocl.ShouldAttemptCompile(
+ /*apex_version=*/1, /*last_update_millis=*/762, OdrMetrics::Trigger::kDexFilesChanged));
+ ASSERT_FALSE(ocl.ShouldAttemptCompile(
+ /*apex_version=*/1, /*last_update_millis=*/762, OdrMetrics::Trigger::kUnknown));
}
TEST(OdrCompilationLog, BackOffNoHistory) {
@@ -117,63 +130,81 @@
OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
ASSERT_TRUE(ocl.ShouldAttemptCompile(
- /*apex_version=*/1, OdrMetrics::Trigger::kApexVersionMismatch, start_time));
+ /*apex_version=*/1,
+ /*last_update_millis=*/0,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time));
// Start log
ocl.Log(/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(ocl.ShouldAttemptCompile(
- /*apex_version=*/1, OdrMetrics::Trigger::kApexVersionMismatch, start_time));
+ /*apex_version=*/1,
+ /*last_update_millis=*/0,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time));
ASSERT_FALSE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + kSecondsPerDay / 2));
ASSERT_TRUE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + kSecondsPerDay));
// Add one more log entry
ocl.Log(/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + kSecondsPerDay));
ASSERT_TRUE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + 2 * kSecondsPerDay));
// One more.
ocl.Log(/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + 3 * kSecondsPerDay));
ASSERT_TRUE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + 4 * kSecondsPerDay));
// And one for the road.
ocl.Log(/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + 7 * kSecondsPerDay));
ASSERT_TRUE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + 8 * kSecondsPerDay));
}
@@ -186,31 +217,40 @@
// Start log with a successful entry.
ocl.Log(/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationSuccess);
ASSERT_FALSE(ocl.ShouldAttemptCompile(
- /*apex_version=*/1, OdrMetrics::Trigger::kApexVersionMismatch, start_time));
+ /*apex_version=*/1,
+ /*last_update_millis=*/0,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time));
ASSERT_FALSE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + kSecondsPerDay / 4));
ASSERT_TRUE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + kSecondsPerDay / 2));
- // Add a log entry for a failed compilation.
+ // Add a log entry for a failed compilation.
ocl.Log(/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
ASSERT_FALSE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + kSecondsPerDay / 2));
ASSERT_TRUE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + kSecondsPerDay));
}
@@ -219,18 +259,19 @@
OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
std::vector<OdrCompilationLogEntry> entries = {
- { 0, 1, 2, 3 },
- { 1, 2, 3, 4 },
- { 2, 3, 4, 5 },
- { 3, 4, 5, 6 },
- { 4, 5, 6, 7 },
- { 5, 6, 7, 8 },
- { 6, 7, 8, 9 }
+ { 0, 1, 2, 3, 4 },
+ { 1, 2, 3, 4, 5 },
+ { 2, 3, 4, 5, 6 },
+ { 3, 4, 5, 6, 7 },
+ { 4, 5, 6, 7, 8 },
+ { 5, 6, 7, 8, 9 },
+ { 6, 7, 8, 9, 10 }
};
for (size_t i = 0; i < entries.size(); ++i) {
OdrCompilationLogEntry& e = entries[i];
ocl.Log(e.apex_version,
+ e.last_update_millis,
static_cast<OdrMetrics::Trigger>(e.trigger),
e.when,
static_cast<ExitCode>(e.exit_code));
@@ -251,13 +292,13 @@
TEST_F(OdrCompilationLogTest, LogReadWrite) {
std::vector<OdrCompilationLogEntry> entries = {
- { 0, 1, 2, 3 },
- { 1, 2, 3, 4 },
- { 2, 3, 4, 5 },
- { 3, 4, 5, 6 },
- { 4, 5, 6, 7 },
- { 5, 6, 7, 8 },
- { 6, 7, 8, 9 }
+ { 0, 1, 2, 3, 4 },
+ { 1, 2, 3, 4, 5 },
+ { 2, 3, 4, 5, 6 },
+ { 3, 4, 5, 6, 7 },
+ { 4, 5, 6, 7, 8 },
+ { 5, 6, 7, 8, 9 },
+ { 6, 7, 8, 9, 10 }
};
ScratchFile scratch_file;
@@ -268,6 +309,7 @@
OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
OdrCompilationLogEntry& e = entries[i];
ocl.Log(e.apex_version,
+ e.last_update_millis,
static_cast<OdrMetrics::Trigger>(e.trigger),
e.when,
static_cast<ExitCode>(e.exit_code));
@@ -303,7 +345,10 @@
OdrCompilationLog ocl(log_path);
ASSERT_TRUE(ocl.ShouldAttemptCompile(
- /*apex_version=*/1, OdrMetrics::Trigger::kApexVersionMismatch, start_time));
+ /*apex_version=*/1,
+ /*last_update_millis=*/0,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time));
}
{
@@ -311,6 +356,7 @@
// Start log
ocl.Log(/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
@@ -319,13 +365,18 @@
{
OdrCompilationLog ocl(log_path);
ASSERT_FALSE(ocl.ShouldAttemptCompile(
- /*apex_version=*/1, OdrMetrics::Trigger::kApexVersionMismatch, start_time));
+ /*apex_version=*/1,
+ /*last_update_millis=*/0,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time));
ASSERT_FALSE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + kSecondsPerDay / 2));
ASSERT_TRUE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + kSecondsPerDay));
}
@@ -334,6 +385,7 @@
// Add one more log entry
OdrCompilationLog ocl(log_path);
ocl.Log(/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
@@ -344,10 +396,12 @@
ASSERT_FALSE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + kSecondsPerDay));
ASSERT_TRUE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + 2 * kSecondsPerDay));
}
@@ -356,27 +410,7 @@
// One more log entry.
OdrCompilationLog ocl(log_path);
ocl.Log(/*apex_version=*/1,
- OdrMetrics::Trigger::kApexVersionMismatch,
- start_time,
- ExitCode::kCompilationFailed);
- }
-
- {
- OdrCompilationLog ocl(log_path);
- ASSERT_FALSE(ocl.ShouldAttemptCompile(
- /*apex_version=*/1,
- OdrMetrics::Trigger::kApexVersionMismatch,
- start_time + 3 * kSecondsPerDay));
- ASSERT_TRUE(ocl.ShouldAttemptCompile(
- /*apex_version=*/1,
- OdrMetrics::Trigger::kApexVersionMismatch,
- start_time + 4 * kSecondsPerDay));
- }
-
- {
- // And one for the road.
- OdrCompilationLog ocl(log_path);
- ocl.Log(/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationFailed);
@@ -386,14 +420,125 @@
OdrCompilationLog ocl(log_path);
ASSERT_FALSE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time + 3 * kSecondsPerDay));
+ ASSERT_TRUE(ocl.ShouldAttemptCompile(
+ /*apex_version=*/1,
+ /*last_update_millis=*/0,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time + 4 * kSecondsPerDay));
+ }
+
+ {
+ // And one for the road.
+ OdrCompilationLog ocl(log_path);
+ ocl.Log(/*apex_version=*/1,
+ /*last_update_millis=*/0,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time,
+ ExitCode::kCompilationFailed);
+ }
+
+ {
+ OdrCompilationLog ocl(log_path);
+ ASSERT_FALSE(ocl.ShouldAttemptCompile(
+ /*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + 7 * kSecondsPerDay));
ASSERT_TRUE(ocl.ShouldAttemptCompile(
/*apex_version=*/1,
+ /*last_update_millis=*/0,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time + 8 * kSecondsPerDay));
}
}
+TEST(OdrCompilationLog, LastUpdateMillisChangeTriggersCompilation) {
+ time_t start_time;
+ time(&start_time);
+
+ OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
+
+ for (int64_t last_update_millis = 0; last_update_millis < 10000; last_update_millis += 1000) {
+ static const int64_t kApexVersion = 19999;
+ ASSERT_TRUE(ocl.ShouldAttemptCompile(
+ kApexVersion, last_update_millis, OdrMetrics::Trigger::kApexVersionMismatch, start_time));
+ ocl.Log(kApexVersion,
+ last_update_millis,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time,
+ ExitCode::kCompilationSuccess);
+ ASSERT_FALSE(ocl.ShouldAttemptCompile(kApexVersion,
+ last_update_millis,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time + 1));
+ }
+}
+
+TEST(OdrCompilationLog, ApexVersionChangeTriggersCompilation) {
+ time_t start_time;
+ time(&start_time);
+
+ OdrCompilationLog ocl(/*compilation_log_path=*/nullptr);
+
+ for (int64_t apex_version = 0; apex_version < 10000; apex_version += 1000) {
+ static const int64_t kLastUpdateMillis = 777;
+ ASSERT_TRUE(ocl.ShouldAttemptCompile(apex_version,
+ kLastUpdateMillis,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time + 8 * kSecondsPerDay));
+ ocl.Log(apex_version,
+ kLastUpdateMillis,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time,
+ ExitCode::kCompilationSuccess);
+ ASSERT_FALSE(ocl.ShouldAttemptCompile(apex_version,
+ kLastUpdateMillis,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time + 1));
+ }
+}
+
+TEST_F(OdrCompilationLogTest, NewLogVersionTriggersCompilation) {
+ static const int64_t kApexVersion = 1066;
+ static const int64_t kLastUpdateMillis = 777;
+ time_t start_time;
+ time(&start_time);
+
+ ScratchFile scratch_file;
+ scratch_file.Close();
+
+ // Generate a compilation log.
+ {
+ OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
+ for (size_t i = 0; i < OdrCompilationLog::kMaxLoggedEntries; ++i) {
+ ocl.Log(kApexVersion,
+ kLastUpdateMillis,
+ OdrMetrics::Trigger::kApexVersionMismatch,
+ start_time,
+ ExitCode::kCompilationSuccess);
+ ASSERT_FALSE(ocl.ShouldAttemptCompile(
+ kApexVersion, kLastUpdateMillis, OdrMetrics::Trigger::kApexVersionMismatch, start_time));
+ }
+ }
+
+ // Replace version string in the compilation log.
+ std::string log_text;
+ ASSERT_TRUE(android::base::ReadFileToString(scratch_file.GetFilename(), &log_text));
+ std::string new_log_version = std::string(OdrCompilationLog::kLogVersion) + "a";
+ log_text.replace(0, new_log_version.size() - 1, new_log_version);
+ ASSERT_TRUE(android::base::WriteStringToFile(log_text, scratch_file.GetFilename()));
+
+ // Read log with updated version entry, check it is treated as out-of-date.
+ {
+ OdrCompilationLog ocl(scratch_file.GetFilename().c_str());
+ ASSERT_TRUE(ocl.ShouldAttemptCompile(
+ kApexVersion, kLastUpdateMillis, OdrMetrics::Trigger::kApexVersionMismatch, start_time));
+ ASSERT_EQ(0u, ocl.NumberOfEntries());
+ }
+}
+
} // namespace odrefresh
} // namespace art
diff --git a/odrefresh/odr_metrics.h b/odrefresh/odr_metrics.h
index 5ff9df2..cd80bef 100644
--- a/odrefresh/odr_metrics.h
+++ b/odrefresh/odr_metrics.h
@@ -75,7 +75,7 @@
~OdrMetrics();
// Gets the ART APEX that metrics are being collected on behalf of.
- int64_t GetApexVersion() const {
+ int64_t GetArtApexVersion() const {
return art_apex_version_;
}
@@ -84,6 +84,16 @@
art_apex_version_ = version;
}
+ // Gets the ART APEX last update time in milliseconds.
+ int64_t GetArtApexLastUpdateMillis() const {
+ return art_apex_last_update_millis_;
+ }
+
+ // Sets the ART APEX last update time in milliseconds.
+ void SetArtApexLastUpdateMillis(int64_t last_update_millis) {
+ art_apex_last_update_millis_ = last_update_millis;
+ }
+
// Gets the trigger for metrics collection. The trigger is the reason why odrefresh considers
// compilation necessary.
Trigger GetTrigger() const {
@@ -122,6 +132,7 @@
const std::string metrics_file_;
int64_t art_apex_version_ = 0;
+ int64_t art_apex_last_update_millis_ = 0;
std::optional<Trigger> trigger_ = {}; // metrics are only logged if compilation is triggered.
Stage stage_ = Stage::kUnknown;
Status status_ = Status::kUnknown;
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index cccf2b3..d3af247 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -360,7 +360,10 @@
LOG(ERROR) << "Could not update " << QuotePath(cache_info_filename_) << " : no ART Apex info";
return {};
}
- return art_apex::ArtModuleInfo{info->getVersionCode(), info->getVersionName()};
+ // The lastUpdateMillis is an addition to ApexInfoList.xsd to support samegrade installs.
+ int64_t last_update_millis = info->hasLastUpdateMillis() ? info->getLastUpdateMillis() : 0;
+ return art_apex::ArtModuleInfo{
+ info->getVersionCode(), info->getVersionName(), last_update_millis};
}
bool CheckComponents(const std::vector<art_apex::Component>& expected_components,
@@ -530,9 +533,12 @@
return cleanup_return(ExitCode::kCompilationRequired);
}
- // Record ART Apex version for metrics reporting.
+ // Record ART APEX version for metrics reporting.
metrics.SetArtApexVersion(current_info->getVersionCode());
+ // Record ART APEX last update milliseconds (used in compilation log).
+ metrics.SetArtApexLastUpdateMillis(current_info->getLastUpdateMillis());
+
// Check whether the current cache ART module info differs from the current ART module info.
// Always check APEX version.
const auto cached_info = cache_info->getFirstArtModuleInfo();
@@ -546,13 +552,25 @@
}
if (cached_info->getVersionName() != current_info->getVersionName()) {
- LOG(INFO) << "ART APEX version code mismatch ("
+ LOG(INFO) << "ART APEX version name mismatch ("
<< cached_info->getVersionName()
<< " != " << current_info->getVersionName() << ").";
metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
return cleanup_return(ExitCode::kCompilationRequired);
}
+ // Check lastUpdateMillis for samegrade installs. If `cached_info` is missing lastUpdateMillis
+ // then it is not current with the schema used by this binary so treat it as a samegrade
+ // update. Otherwise check whether the lastUpdateMillis changed.
+ if (!cached_info->hasLastUpdateMillis() ||
+ cached_info->getLastUpdateMillis() != current_info->getLastUpdateMillis()) {
+ LOG(INFO) << "ART APEX last update time mismatch ("
+ << cached_info->getLastUpdateMillis()
+ << " != " << current_info->getLastUpdateMillis() << ").";
+ metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
+ return cleanup_return(ExitCode::kCompilationRequired);
+ }
+
// Check boot class components.
//
// This checks the size and checksums of odrefresh compilable files on the DEX2OATBOOTCLASSPATH
@@ -1449,11 +1467,16 @@
return exit_code;
}
OdrCompilationLog compilation_log;
- if (!compilation_log.ShouldAttemptCompile(metrics.GetApexVersion(), metrics.GetTrigger())) {
+ if (!compilation_log.ShouldAttemptCompile(metrics.GetArtApexVersion(),
+ metrics.GetArtApexLastUpdateMillis(),
+ metrics.GetTrigger())) {
return ExitCode::kOkay;
}
ExitCode compile_result = odr.Compile(metrics, /*force_compile=*/false);
- compilation_log.Log(metrics.GetApexVersion(), metrics.GetTrigger(), compile_result);
+ compilation_log.Log(metrics.GetArtApexVersion(),
+ metrics.GetArtApexLastUpdateMillis(),
+ metrics.GetTrigger(),
+ compile_result);
return compile_result;
} else if (action == "--force-compile") {
return odr.Compile(metrics, /*force_compile=*/true);
diff --git a/odrefresh/schema/current.txt b/odrefresh/schema/current.txt
index ae10712..e6933f6 100644
--- a/odrefresh/schema/current.txt
+++ b/odrefresh/schema/current.txt
@@ -3,8 +3,10 @@
public class ArtModuleInfo {
ctor public ArtModuleInfo();
+ method public long getLastUpdateMillis();
method public long getVersionCode();
method public String getVersionName();
+ method public void setLastUpdateMillis(long);
method public void setVersionCode(long);
method public void setVersionName(String);
}