Merge "Delete finalized apexd sessions after boot complete"
diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp
index 5de713c..75fe076 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -2239,6 +2239,7 @@
 void bootCompletedCleanup() {
   UnmountDanglingMounts();
   RemoveOrphanedApexes();
+  ApexSession::DeleteFinalizedSessions();
 }
 
 int unmountAll() {
diff --git a/apexd/apexd_session.cpp b/apexd/apexd_session.cpp
index 3d916c4..8ca5fa8 100644
--- a/apexd/apexd_session.cpp
+++ b/apexd/apexd_session.cpp
@@ -259,5 +259,18 @@
              << "]";
 }
 
+void ApexSession::DeleteFinalizedSessions() {
+  auto sessions = GetSessions();
+  for (const ApexSession& session : sessions) {
+    if (!session.IsFinalized()) {
+      continue;
+    }
+    auto result = session.DeleteSession();
+    if (!result.ok()) {
+      LOG(WARNING) << "Failed to delete finalized session: " << session.GetId();
+    }
+  }
+}
+
 }  // namespace apex
 }  // namespace android
diff --git a/apexd/apexd_session.h b/apexd/apexd_session.h
index e0cea91..ed045a7 100644
--- a/apexd/apexd_session.h
+++ b/apexd/apexd_session.h
@@ -64,6 +64,7 @@
       const ::apex::proto::SessionState::State& state);
 
   android::base::Result<void> DeleteSession() const;
+  static void DeleteFinalizedSessions();
 
  private:
   ApexSession(::apex::proto::SessionState state);
diff --git a/apexd/apexservice_test.cpp b/apexd/apexservice_test.cpp
index 5dc2161..1e09aaf 100644
--- a/apexd/apexservice_test.cpp
+++ b/apexd/apexservice_test.cpp
@@ -1983,6 +1983,42 @@
                                              SessionInfoEq(expected2)));
 }
 
+// Only finalized sessions should be deleted on DeleteFinalizedSessions()
+TEST_F(ApexServiceTest, DeleteFinalizedSessions) {
+  // Fetch list of all session state
+  std::vector<SessionState::State> states;
+  for (int i = SessionState::State_MIN; i < SessionState::State_MAX; i++) {
+    if (!SessionState::State_IsValid(i)) {
+      continue;
+    }
+    states.push_back(SessionState::State(i));
+  }
+
+  // For every session state, create a new session. This is to verify we only
+  // delete sessions in final state.
+  auto nonFinalSessions = 0u;
+  for (auto i = 0u; i < states.size(); i++) {
+    auto session = ApexSession::CreateSession(230 + i);
+    SessionState::State state = states[i];
+    ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(state)));
+    if (!session->IsFinalized()) {
+      nonFinalSessions++;
+    }
+  }
+  std::vector<ApexSession> sessions = ApexSession::GetSessions();
+  ASSERT_EQ(states.size(), sessions.size());
+
+  // Now try cleaning up all finalized sessions
+  ApexSession::DeleteFinalizedSessions();
+  sessions = ApexSession::GetSessions();
+  ASSERT_EQ(nonFinalSessions, sessions.size());
+
+  // Verify only finalized sessions have been deleted
+  for (auto& session : sessions) {
+    ASSERT_FALSE(session.IsFinalized());
+  }
+}
+
 TEST_F(ApexServiceTest, BackupActivePackages) {
   if (supports_fs_checkpointing_) {
     GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled";