Add build fingerprint checks to apexd

Staged sessions should be abandoned when the build
fingerprint changes (in case of an OTA, for example).
This change stores the build fingerprint as part of an
APEX session, which is then verified upon reboot.

Bug: 145840157
Test: Stage APEX install, reboot with same/different build
      fingerprints and verify session is completed/abandoned
      respectively
Test: atest ApexServiceTest#SubmitStagedSessionStoresBuildFingerprint
Change-Id: I90ff014e82e6913914f0d752bb43f10d5efc6f2c
Merged-In: I90ff014e82e6913914f0d752bb43f10d5efc6f2c
(cherry picked from commit 778cacee8bffc8d257245e71c05f705fa53078c1)
diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp
index 4f5ca52..589b962 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -93,6 +93,8 @@
 static constexpr const char* kApexStatusStarting = "starting";
 static constexpr const char* kApexStatusReady = "ready";
 
+static constexpr const char* kBuildFingerprintSysprop = "ro.build.fingerprint";
+
 static constexpr const char* kApexVerityOnSystemProp =
     "persist.apexd.verity_on_system";
 static bool gForceDmVerityOnSystem =
@@ -1372,6 +1374,7 @@
 }
 
 void scanStagedSessionsDirAndStage() {
+  using android::base::GetProperty;
   LOG(INFO) << "Scanning " << kApexSessionsDir
             << " looking for sessions to be activated.";
 
@@ -1389,6 +1392,12 @@
     };
     auto scope_guard = android::base::make_scope_guard(session_failed_fn);
 
+    std::string build_fingerprint = GetProperty(kBuildFingerprintSysprop, "");
+    if (session.GetBuildFingerprint().compare(build_fingerprint) != 0) {
+      LOG(ERROR) << "APEX build fingerprint has changed";
+      continue;
+    }
+
     std::vector<std::string> dirsToScan;
     if (session.GetChildSessionIds().empty()) {
       dirsToScan.push_back(std::string(kStagedSessionsDir) + "/session_" +
@@ -1789,6 +1798,7 @@
 
 StatusOr<std::vector<ApexFile>> submitStagedSession(
     const int session_id, const std::vector<int>& child_session_ids) {
+  using android::base::GetProperty;
   bool needsBackup = true;
   Status cleanup_status = ClearSessions();
   if (!cleanup_status.Ok()) {
@@ -1843,6 +1853,8 @@
     return StatusOr<std::vector<ApexFile>>::MakeError(session.ErrorMessage());
   }
   (*session).SetChildSessionIds(child_session_ids);
+  std::string build_fingerprint = GetProperty(kBuildFingerprintSysprop, "");
+  (*session).SetBuildFingerprint(build_fingerprint);
   Status commit_status =
       (*session).UpdateStateAndCommit(SessionState::VERIFIED);
   if (!commit_status.Ok()) {
diff --git a/apexd/apexd_session.cpp b/apexd/apexd_session.cpp
index c94585f..98180b7 100644
--- a/apexd/apexd_session.cpp
+++ b/apexd/apexd_session.cpp
@@ -168,6 +168,10 @@
 
 int ApexSession::GetId() const { return state_.id(); }
 
+std::string ApexSession::GetBuildFingerprint() const {
+  return state_.expected_build_fingerprint();
+}
+
 bool ApexSession::IsFinalized() const {
   switch (GetState()) {
     case SessionState::SUCCESS:
@@ -194,6 +198,10 @@
                                            child_session_ids.end()};
 }
 
+void ApexSession::SetBuildFingerprint(const std::string& fingerprint) {
+  *(state_.mutable_expected_build_fingerprint()) = fingerprint;
+}
+
 Status ApexSession::UpdateStateAndCommit(
     const SessionState::State& session_state) {
   state_.set_state(session_state);
diff --git a/apexd/apexd_session.h b/apexd/apexd_session.h
index 5f32198..7b6b703 100644
--- a/apexd/apexd_session.h
+++ b/apexd/apexd_session.h
@@ -43,9 +43,11 @@
   const google::protobuf::RepeatedField<int> GetChildSessionIds() const;
   ::apex::proto::SessionState::State GetState() const;
   int GetId() const;
+  std::string GetBuildFingerprint() const;
   bool IsFinalized() const;
 
   void SetChildSessionIds(const std::vector<int>& child_session_ids);
+  void SetBuildFingerprint(const std::string& fingerprint);
   Status UpdateStateAndCommit(const ::apex::proto::SessionState::State& state);
 
   Status DeleteSession() const;
diff --git a/apexd/apexservice_test.cpp b/apexd/apexservice_test.cpp
index cad7789..89ca62b 100644
--- a/apexd/apexservice_test.cpp
+++ b/apexd/apexservice_test.cpp
@@ -565,6 +565,21 @@
   }
 }
 
+TEST_F(ApexServiceTest, SubmitStagedSessionStoresBuildFingerprint) {
+  PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test.apex"),
+                                      "/data/app-staging/session_1547",
+                                      "staging_data_file");
+  if (!installer.Prepare()) {
+    return;
+  }
+  ApexInfoList list;
+  bool success;
+  ASSERT_TRUE(IsOk(service_->submitStagedSession(1547, {}, &list, &success)));
+
+  auto session = ApexSession::GetSession(1547);
+  ASSERT_FALSE(session->GetBuildFingerprint().empty());
+}
+
 TEST_F(ApexServiceTest, SubmitStagedSessionFailDoesNotLeakTempVerityDevices) {
   using android::dm::DeviceMapper;
 
diff --git a/proto/session_state.proto b/proto/session_state.proto
index addb703..f023353 100644
--- a/proto/session_state.proto
+++ b/proto/session_state.proto
@@ -39,4 +39,7 @@
 
   // Child session ids
   repeated int32 child_session_ids = 3;
+
+  // Build fingerprint before reboot
+  string expected_build_fingerprint = 4;
 }