iorap: Add version support to iorap.

To avoid package query IPC overhead, when iorapd is starting, all
package version are cached (~80ms) in RAM for faster queries. If the app
in newly installed and the version is not cached, one IPC call is
made with a cost ~0.6ms and the result is cached.

The RAM versions is updated during maintenance.

Details:
common: Add version support to iorap.
manager: Add version support for perfetto trace insertion.
maintenance: Update the package versions during maintenance.
maintenance: Delete older version packages and relevant data and files.
maintenance: Add version to compilation in maintenance.
db: Add delete cascade.

Bug: 137785555
Test: Run on the device and the vesrion is shown in database after
insertion.
Change-Id: If9a2f68888d81fa7be34ef6e31a0a972f7548f3b
diff --git a/Android.bp b/Android.bp
index 8335932..ab8f513 100644
--- a/Android.bp
+++ b/Android.bp
@@ -108,6 +108,8 @@
     srcs: [
         ":iorap-aidl",
         "src/binder/iiorap_impl.cc",
+        "src/binder/package_manager_remote.cc",
+        "src/binder/package_version_map.cc",
     ],
     shared_libs: [
         "libbinder",
@@ -125,10 +127,10 @@
     ],
 }
 
-
 cc_defaults {
     name: "libiorap-manager-default-dependencies",
     static_libs: [
+        "libiorap-binder",
         "libiorap-perfetto",
         "libiorap-prefetcher",
         "libiorap-db",
@@ -673,6 +675,7 @@
     include_dirs: [],
 
     static_libs: [
+        "libiorap-binder",
         "libiorap-compiler",
         "libiorap-db",
     ],
diff --git a/src/binder/package_manager_remote.cc b/src/binder/package_manager_remote.cc
new file mode 100644
index 0000000..da56a17
--- /dev/null
+++ b/src/binder/package_manager_remote.cc
@@ -0,0 +1,84 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "package_manager_remote.h"
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+namespace iorap::binder {
+
+std::shared_ptr<PackageManagerRemote> PackageManagerRemote::Create() {
+  android::sp<IPackageManager> package_service = GetPackageService();
+  if (package_service == nullptr) {
+    return nullptr;
+  }
+  return std::make_shared<PackageManagerRemote>(package_service);
+}
+
+android::sp<IPackageManager> PackageManagerRemote::GetPackageService() {
+  auto binder = android::defaultServiceManager()->getService(
+      android::String16{"package_native"});
+  if (binder == nullptr) {
+    LOG(ERROR) << "Cannot get package manager service!";
+    return nullptr;
+  }
+
+  return android::interface_cast<IPackageManager>(binder);
+}
+
+std::optional<int64_t> PackageManagerRemote::GetPackageVersion(
+    const std::string& package_name) {
+  int64_t version_code;
+  auto status = package_service_->getVersionCodeForPackage(
+      android::String16(package_name.c_str()), &version_code);
+  if (!status.isOk()) {
+    LOG(WARNING) << "Failed to get version: " << status.toString8().c_str()
+                 << " for " << package_name;
+    return std::nullopt;
+  }
+
+  return std::optional<int64_t>{version_code};
+}
+
+VersionMap PackageManagerRemote::GetPackageVersionMap() {
+  VersionMap package_version_map;
+
+  std::vector<std::string> packages = GetAllPackages();
+  LOG(DEBUG) << "PackageManagerRemote::GetPackageVersionMap: "
+             << packages.size()
+             << " packages are found.";
+
+  for (std::string package : packages) {
+    std::optional<int64_t> version = GetPackageVersion(package);
+    if (!version) {
+      LOG(DEBUG) << "Cannot get version for " << package;
+      continue;
+    }
+    package_version_map[package] = *version;
+  }
+
+  return package_version_map;
+}
+
+std::vector<std::string> PackageManagerRemote::GetAllPackages() {
+  CHECK(package_service_ != nullptr);
+  std::vector<std::string> packages;
+  auto status = package_service_->getAllPackages(/*out*/&packages);
+  if (!status.isOk()) {
+    LOG(ERROR) << "Failed to get all packages: " << status.toString8().c_str();
+  }
+  return packages;
+}
+}  // namespace iorap::package_manager
diff --git a/src/binder/package_manager_remote.h b/src/binder/package_manager_remote.h
new file mode 100644
index 0000000..e0dd212
--- /dev/null
+++ b/src/binder/package_manager_remote.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef IORAP_SRC_PACKAGE_MANAGER_REMOTE_H_
+#define IORAP_SRC_PACKAGE_MANAGER_REMOTE_H_
+
+#include <android/content/pm/IPackageManagerNative.h>
+#include <binder/IServiceManager.h>
+
+#include <optional>
+#include <unordered_map>
+
+namespace iorap::binder {
+
+using IPackageManager = android::content::pm::IPackageManagerNative;
+// A map between package name and its version.
+using VersionMap = std::unordered_map<std::string, int64_t>;
+
+class PackageManagerRemote {
+ public:
+  static std::shared_ptr<PackageManagerRemote> Create();
+
+  PackageManagerRemote(android::sp<IPackageManager> package_service)
+      : package_service_(package_service) {}
+
+  // Gets the package version based on the package name.
+  std::optional<int64_t> GetPackageVersion(const std::string& package_name);
+
+  // Gets a map of package name and its version.
+  VersionMap GetPackageVersionMap();
+
+ private:
+  // Gets the package manager service.
+  static android::sp<IPackageManager> GetPackageService();
+
+  // Gets all package names.
+  std::vector<std::string> GetAllPackages();
+
+  android::sp<IPackageManager> package_service_;
+};
+}  // namespace iorap::package_manager
+
+#endif  // IORAP_SRC_PACKAGE_MANAGER_REMOTE_H_
diff --git a/src/binder/package_version_map.cc b/src/binder/package_version_map.cc
new file mode 100644
index 0000000..6f64d7b
--- /dev/null
+++ b/src/binder/package_version_map.cc
@@ -0,0 +1,66 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "package_version_map.h"
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+namespace iorap::binder {
+
+std::shared_ptr<PackageVersionMap> PackageVersionMap::Create() {
+  std::shared_ptr<PackageManagerRemote> package_manager =
+      PackageManagerRemote::Create();
+  if (!package_manager) {
+    return nullptr;
+  }
+
+  VersionMap map = package_manager->GetPackageVersionMap();
+
+  return std::make_shared<PackageVersionMap>(package_manager, map);
+}
+
+void PackageVersionMap::Update() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  size_t old_size = version_map_.size();
+  version_map_ = package_manager_->GetPackageVersionMap();
+  LOG(DEBUG) << "Update for version is done. The size is from " << old_size
+             << " to " << version_map_.size();
+}
+
+size_t PackageVersionMap::Size() { return version_map_.size(); }
+
+int64_t PackageVersionMap::GetOrQueryPackageVersion(const std::string& package_name) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  VersionMap::iterator it = version_map_.find(package_name);
+
+  if (it == version_map_.end()) {
+    LOG(WARNING) << "Cannot find version for: " << package_name
+                 << " in the hash table";
+    std::optional<int64_t> version =
+        package_manager_->GetPackageVersion(package_name);
+    if (version) {
+      LOG(VERBOSE) << "Find version for: " << package_name << " on the fly.";
+      version_map_[package_name] = *version;
+      return *version;
+    } else {
+      LOG(ERROR) << "Cannot find version for: " << package_name
+                 << " on the fly.";
+      return -1;
+    }
+  }
+
+  return it->second;
+}
+}  // namespace iorap::binder
diff --git a/src/binder/package_version_map.h b/src/binder/package_version_map.h
new file mode 100644
index 0000000..1c756a8
--- /dev/null
+++ b/src/binder/package_version_map.h
@@ -0,0 +1,70 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef IORAP_SRC_PACKAGE_VERSION_MAP_H_
+#define IORAP_SRC_PACKAGE_VERSION_MAP_H_
+
+#include <android/content/pm/IPackageManagerNative.h>
+#include <binder/IServiceManager.h>
+
+#include <optional>
+#include <unordered_map>
+
+#include "package_manager_remote.h"
+
+namespace iorap::binder {
+
+class PackageVersionMap {
+ public:
+  static std::shared_ptr<PackageVersionMap> Create();
+
+  PackageVersionMap(std::shared_ptr<PackageManagerRemote> package_manager,
+                    const VersionMap& version_map)
+      : package_manager_(package_manager),
+        version_map_(version_map) {}
+
+  void Update();
+
+  // Finds the version of the package in the hash table.
+  // -1 means the app is installed by unversioned.
+  // Empty means the app is not inside the RAM version map, maybe due to
+  // the app is newly installed.
+  std::optional<int64_t> Find(const std::string& package_name) {
+    VersionMap::iterator it = version_map_.find(package_name);
+    if (it == version_map_.end()) {
+      return std::nullopt;
+    }
+    return it->second;
+  }
+
+  size_t Size();
+
+  // Gets or queries the version for the package.
+  //
+  // The method firstly access the hash map in the RAM, which is built when
+  // iorapd starts. If the version is not in the map, it tries the query
+  // the package manager via IPC, with a cost of ~0.6ms.
+  //
+  // If no version can be found for some reason, return -1,
+  // because when an app has no version the package manager returns -1.
+  int64_t GetOrQueryPackageVersion(const std::string& package_name);
+
+ private:
+  std::shared_ptr<PackageManagerRemote> package_manager_;
+  VersionMap version_map_;
+  std::mutex mutex_;
+};
+}  // namespace iorap::binder
+
+#endif  // IORAP_SRC_PACKAGE_MANAGER_REMOTE_H_
diff --git a/src/db/clean_up.cc b/src/db/clean_up.cc
new file mode 100644
index 0000000..b3eba46
--- /dev/null
+++ b/src/db/clean_up.cc
@@ -0,0 +1,78 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "db/clean_up.h"
+
+#include <android-base/file.h>
+
+#include <cstdio>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "db/file_models.h"
+#include "db/models.h"
+
+namespace iorap::db {
+
+void CleanUpFilesForActivity(const db::DbHandle& db,
+                             const db::VersionedComponentName& vcn) {
+  LOG(DEBUG) << "Clean up files for activity " << vcn.GetActivity();
+  // Remove perfetto traces.
+  std::vector<db::RawTraceModel> raw_traces =
+      db::RawTraceModel::SelectByVersionedComponentName(db, vcn);
+  for (db::RawTraceModel raw_trace : raw_traces) {
+    std::string file_path = raw_trace.file_path;
+    LOG(DEBUG) << "Remove file: " << file_path;
+    std::filesystem::remove(file_path.c_str());
+  }
+
+  // Remove compiled traces.
+  std::optional<db::PrefetchFileModel> prefetch_file =
+      db::PrefetchFileModel::SelectByVersionedComponentName(db, vcn);
+
+  if (prefetch_file) {
+    std::string file_path = prefetch_file->file_path;
+    LOG(DEBUG) << "Remove file: " << file_path;
+    std::filesystem::remove(file_path.c_str());
+  }
+}
+
+void CleanUpFilesForPackage(const db::DbHandle& db,
+                            int package_id,
+                            const std::string& package_name,
+                            int64_t version) {
+  LOG(DEBUG) << "Clean up files for package " << package_name << " with version "
+             << version;
+  std::vector<db::ActivityModel> activities =
+      db::ActivityModel::SelectByPackageId(db, package_id);
+
+  for (db::ActivityModel activity : activities) {
+    db::VersionedComponentName vcn{package_name, activity.name, version};
+    CleanUpFilesForActivity(db, vcn);
+  }
+}
+
+void CleanUpFilesForDb(const db::DbHandle& db) {
+  std::vector<db::PackageModel> packages = db::PackageModel::SelectAll(db);
+  for (db::PackageModel package : packages) {
+      CleanUpFilesForPackage(db, package.id, package.name, package.version);
+  }
+}
+
+}  // namespace iorap::db
diff --git a/src/db/clean_up.h b/src/db/clean_up.h
new file mode 100644
index 0000000..82a26fd
--- /dev/null
+++ b/src/db/clean_up.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef IORAP_SRC_DB_CLEANER_H_
+#define IORAP_SRC_DB_CLEANER_H_
+
+#include <android/content/pm/IPackageManagerNative.h>
+
+#include <string>
+#include <vector>
+
+#include "binder/package_version_map.h"
+#include "db/file_models.h"
+
+namespace iorap::db {
+
+// Clean up perfetto traces and compiled traces stored in the db.
+void CleanUpFilesForDb(const db::DbHandle& db);
+
+void CleanUpFilesForPackage(const db::DbHandle& db,
+                            int package_id,
+                            const std::string& package_name,
+                            int64_t version);
+}  // namespace iorap::db
+
+#endif  // IORAP_SRC_DB_CLEANER_H_
diff --git a/src/db/file_models.cc b/src/db/file_models.cc
index f47c68b..9a0c360 100644
--- a/src/db/file_models.cc
+++ b/src/db/file_models.cc
@@ -87,11 +87,7 @@
   std::stringstream ss;
 
   ss << root_path_ << "/" << vcn_.GetPackage() << "/";
-  if (vcn_.GetVersion()) {
-    ss << *vcn_.GetVersion();
-  } else {
-    ss << "none";
-  }
+  ss << vcn_.GetVersion();
   ss << "/";
   ss << vcn_.GetActivity() << "/";
   ss << SubDir();
@@ -131,9 +127,7 @@
 
 void PerfettoTraceFileModel::DeleteOlderFiles(DbHandle& db, VersionedComponentName vcn) {
   std::vector<RawTraceModel> raw_traces =
-      RawTraceModel::SelectByPackageNameActivityName(db,
-                                                     vcn.GetPackage(),
-                                                     vcn.GetActivity());  // TODO: version
+      RawTraceModel::SelectByVersionedComponentName(db, vcn);
 
   if (WOULD_LOG(VERBOSE)) {
     size_t raw_traces_size = raw_traces.size();
diff --git a/src/db/file_models.h b/src/db/file_models.h
index 0a2c24f..135c98a 100644
--- a/src/db/file_models.h
+++ b/src/db/file_models.h
@@ -24,7 +24,7 @@
 struct VersionedComponentName {
   VersionedComponentName(std::string package,
                          std::string activity,
-                         std::optional<int> version)
+                         int64_t version)
     : package_{std::move(package)},
       activity_{std::move(activity)},
       version_{version} {
@@ -38,23 +38,18 @@
     return activity_;
   }
 
-  std::optional<int> GetVersion() const {
+  int GetVersion() const {
     return version_;
   }
 
  private:
   std::string package_;
   std::string activity_;
-  std::optional<int> version_;
+  int64_t version_;
 };
 
 inline std::ostream& operator<<(std::ostream& os, const VersionedComponentName& vcn) {
-  os << vcn.GetPackage() << "/" << vcn.GetActivity() << "@";
-  if (vcn.GetVersion()) {
-    os << "none";
-  } else {
-    os << *vcn.GetVersion();
-  }
+  os << vcn.GetPackage() << "/" << vcn.GetActivity() << "@" << vcn.GetVersion();
   return os;
 }
 
diff --git a/src/db/main.cc b/src/db/main.cc
index 7b64756..2edd961 100644
--- a/src/db/main.cc
+++ b/src/db/main.cc
@@ -54,6 +54,8 @@
   LOG(ERROR) << "SQLite error (" << iErrCode << "): " << zMsg;
 }
 
+const constexpr int64_t kNoVersion = -1;
+
 int Main(int argc, char** argv) {
   // Go to system logcat + stderr when running from command line.
   android::base::InitLogging(argv, iorap::common::StderrAndLogdLogger{android::base::SYSTEM});
@@ -186,7 +188,7 @@
       std::optional<ActivityModel> activity =
           ActivityModel::SelectOrInsert(db,
                                         component_name.package,
-                                        /*version*/std::nullopt,
+                                        kNoVersion,
                                         component_name.activity_name);
       DCHECK(activity.has_value());
       LOG(DEBUG) << "Component selected/inserted: " << *activity;
@@ -204,7 +206,7 @@
       std::optional<ActivityModel> activity =
           ActivityModel::SelectOrInsert(db,
                                         component_name.package,
-                                        /*version*/std::nullopt,
+                                        kNoVersion,
                                         component_name.activity_name);
 
       DCHECK(activity.has_value());
diff --git a/src/db/models.h b/src/db/models.h
index 90acfc1..bfe8cfc 100644
--- a/src/db/models.h
+++ b/src/db/models.h
@@ -15,8 +15,14 @@
 #ifndef IORAP_SRC_DB_MODELS_H_
 #define IORAP_SRC_DB_MODELS_H_
 
-#include <android-base/logging.h>
+#include "clean_up.h"
+#include "file_models.h"
 
+#include <android-base/logging.h>
+#include <utils/String8.h>
+
+#include <filesystem>
+#include <iostream>
 #include <optional>
 #include <ostream>
 #include <string>
@@ -28,6 +34,8 @@
 
 namespace iorap::db {
 
+const constexpr int kDbVersion = 2;
+
 struct SqliteDbDeleter {
   void operator()(sqlite3* db) {
     if (db != nullptr) {
@@ -368,16 +376,34 @@
     }
 
     sqlite3* db = nullptr;
+    bool is_deprecated = false;
     if (location != ":memory:") {
       // Try to open DB if it already exists.
       rc = sqlite3_open_v2(location.c_str(), /*out*/&db, SQLITE_OPEN_READWRITE, /*vfs*/nullptr);
 
       if (rc == SQLITE_OK) {
         LOG(INFO) << "Opened existing database at '" << location << "'";
-        return SchemaModel{DbHandle{db}, location};
+        SchemaModel schema{DbHandle{db}, location};
+        if (schema.Version() == kDbVersion) {
+          return schema;
+        } else {
+          LOG(DEBUG) << "The version is old, reinit the db."
+                     << " old version is "
+                     << schema.Version()
+                     << " and new version is "
+                     << kDbVersion;
+          CleanUpFilesForDb(schema.db());
+          is_deprecated = true;
+       }
       }
     }
 
+    if (is_deprecated) {
+      // Remove the db and recreate it.
+      // TODO: migrate to a newer version without deleting the old one.
+      std::filesystem::remove(location.c_str());
+    }
+
     // Create a new DB if one didn't exist already.
     rc = sqlite3_open(location.c_str(), /*out*/&db);
 
@@ -452,12 +478,11 @@
         CREATE TABLE schema_versions(
             version INTEGER NOT NULL
         );
-        INSERT INTO schema_versions VALUES(1);
 
         CREATE TABLE packages(
             id INTEGER NOT NULL,
             name TEXT NOT NULL,
-            version INTEGER,
+            version INTEGER NOT NULL,
 
             PRIMARY KEY(id)
         );
@@ -468,7 +493,7 @@
             package_id INTEGER NOT NULL,
 
             PRIMARY KEY(id),
-            FOREIGN KEY (package_id) REFERENCES packages (id)
+            FOREIGN KEY (package_id) REFERENCES packages (id) ON DELETE CASCADE
         );
 
         CREATE TABLE app_launch_histories(
@@ -485,7 +510,7 @@
             -- absolute timestamp since epoch
             report_fully_drawn_ns INTEGER CHECK(report_fully_drawn_ns IS NULL or report_fully_drawn_ns >= 0),
 
-            FOREIGN KEY (activity_id) REFERENCES activities (id)
+            FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE
         );
 
         CREATE TABLE raw_traces(
@@ -493,7 +518,7 @@
             history_id INTEGER NOT NULL,
             file_path TEXT NOT NULL,
 
-            FOREIGN KEY (history_id) REFERENCES app_launch_histories (id)
+            FOREIGN KEY (history_id) REFERENCES app_launch_histories (id) ON DELETE CASCADE
         );
 
         CREATE TABLE prefetch_files(
@@ -501,7 +526,7 @@
           activity_id INTEGER NOT NULL,
           file_path TEXT NOT NULL,
 
-          FOREIGN KEY (activity_id) REFERENCES activities (id)
+          FOREIGN KEY (activity_id) REFERENCES activities (id) ON DELETE CASCADE
         );
 )SQLC0D3";
 
@@ -515,6 +540,21 @@
     if (rc != SQLITE_OK) {
       LOG(FATAL) << "Failed to create tables: " << err_msg ? err_msg : "nullptr";
     }
+
+    const char* sql_to_insert_schema_version = R"SQLC0D3(
+      INSERT INTO schema_versions VALUES(%d)
+      )SQLC0D3";
+    rc = sqlite3_exec(db().get(),
+                      android::String8::format(sql_to_insert_schema_version,
+                                               kDbVersion),
+                      /*callback*/nullptr,
+                      /*arg*/0,
+                      /*out*/&err_msg);
+
+    if (rc != SQLITE_OK) {
+      LOG(FATAL) << "Failed to insert the schema version: "
+                 << err_msg ? err_msg : "nullptr";
+    }
   }
 
   static void ErrorLogCallback(void *pArg, int iErrCode, const char *zMsg) {
@@ -559,6 +599,23 @@
     return p;
   }
 
+  static std::optional<PackageModel> SelectByNameAndVersion(DbHandle db,
+                                                            const char* name,
+                                                            int version) {
+    ScopedLockDb lock{db};
+
+    std::string query =
+        "SELECT * FROM packages WHERE name = ?1 AND version = ?2 LIMIT 1;";
+    DbStatement stmt = DbStatement::Prepare(db, query, name, version);
+
+    PackageModel p{db};
+    if (!DbQueryBuilder::SelectOnce(stmt, p.id, p.name, p.version)) {
+      return std::nullopt;
+    }
+
+    return p;
+  }
+
   static std::vector<PackageModel> SelectAll(DbHandle db) {
     ScopedLockDb lock{db};
 
@@ -576,7 +633,7 @@
 
   static std::optional<PackageModel> Insert(DbHandle db,
                                             std::string name,
-                                            std::optional<int> version) {
+                                            int version) {
     const char* sql = "INSERT INTO packages (name, version) VALUES (?1, ?2);";
 
     std::optional<int> inserted_row_id =
@@ -593,19 +650,21 @@
     return p;
   }
 
+  bool Delete() {
+    const char* sql = "DELETE FROM packages WHERE id = ?";
+
+    return DbQueryBuilder::Delete(db(), sql, id);
+  }
+
   int id;
   std::string name;
-  std::optional<int> version;
+  int version;
 };
 
 inline std::ostream& operator<<(std::ostream& os, const PackageModel& p) {
   os << "PackageModel{id=" << p.id << ",name=" << p.name << ",";
   os << "version=";
-  if (p.version) {
-    os << *p.version;
-  } else {
-    os << "(nullopt)";
-  }
+  os << p.version;
   os << "}";
   return os;
 }
@@ -687,9 +746,11 @@
   static std::optional<ActivityModel> SelectOrInsert(
       DbHandle db,
       std::string package_name,
-      std::optional<int> package_version,
+      int package_version,
       std::string activity_name) {
-    std::optional<PackageModel> package = PackageModel::SelectByName(db, package_name.c_str());
+    std::optional<PackageModel> package = PackageModel::SelectByNameAndVersion(db,
+                                                                               package_name.c_str(),
+                                                                               package_version);
     if (!package) {
       package = PackageModel::Insert(db, package_name, package_version);
       DCHECK(package.has_value());
@@ -892,9 +953,8 @@
  public:
 
   // Return raw_traces, sorted ascending by the id.
-  static std::vector<RawTraceModel> SelectByPackageNameActivityName(DbHandle db,
-                                                                    std::string package_name,
-                                                                    std::string activity_name) {
+  static std::vector<RawTraceModel> SelectByVersionedComponentName(DbHandle db,
+                                                                   VersionedComponentName vcn) {
     ScopedLockDb lock{db};
 
     const char* sql =
@@ -903,10 +963,14 @@
       "INNER JOIN app_launch_histories ON raw_traces.history_id = app_launch_histories.id "
       "INNER JOIN activities ON activities.id = app_launch_histories.activity_id "
       "INNER JOIN packages ON packages.id = activities.package_id "
-      "WHERE packages.name = ? AND activities.name = ? "
+      "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?"
       "ORDER BY raw_traces.id ASC";
 
-    DbStatement stmt = DbStatement::Prepare(db, sql, package_name, activity_name);
+    DbStatement stmt = DbStatement::Prepare(db,
+                                            sql,
+                                            vcn.GetPackage(),
+                                            vcn.GetActivity(),
+                                            vcn.GetVersion());
 
     std::vector<RawTraceModel> results;
 
@@ -979,8 +1043,9 @@
   }
 
  public:
-  static std::optional<PrefetchFileModel> SelectByPackageNameActivityName(
-      DbHandle db, const std::string& package_name, const std::string& activity_name) {
+  static std::optional<PrefetchFileModel> SelectByVersionedComponentName(
+      DbHandle db,
+      VersionedComponentName vcn) {
     ScopedLockDb lock{db};
 
     const char* sql =
@@ -988,9 +1053,13 @@
       "FROM prefetch_files "
       "INNER JOIN activities ON activities.id = prefetch_files.activity_id "
       "INNER JOIN packages ON packages.id = activities.package_id "
-      "WHERE packages.name = ? AND activities.name = ? ";
+      "WHERE packages.name = ? AND activities.name = ? AND packages.version = ?";
 
-    DbStatement stmt = DbStatement::Prepare(db, sql, package_name, activity_name);
+    DbStatement stmt = DbStatement::Prepare(db,
+                                            sql,
+                                            vcn.GetPackage(),
+                                            vcn.GetActivity(),
+                                            vcn.GetVersion());
 
     PrefetchFileModel p{db};
 
diff --git a/src/maintenance/controller.cc b/src/maintenance/controller.cc
index 818fb13..6c91ed1 100644
--- a/src/maintenance/controller.cc
+++ b/src/maintenance/controller.cc
@@ -39,7 +39,7 @@
 db::CompiledTraceFileModel CalculateNewestFilePath(
     const std::string& package_name,
     const std::string& activity_name,
-    const std::optional<int> version) {
+    int version) {
    db::VersionedComponentName versioned_component_name{
      package_name, activity_name, version};
 
@@ -195,9 +195,10 @@
                      int package_id,
                      const std::string& package_name,
                      const std::string& activity_name,
+                     int version,
                      const ControllerParameters& params) {
   db::CompiledTraceFileModel output_file =
-      CalculateNewestFilePath(package_name, activity_name, /* version= */std::nullopt);
+      CalculateNewestFilePath(package_name, activity_name, version);
 
   std::string file_path = output_file.FilePath();
 
@@ -233,6 +234,7 @@
     LOG(DEBUG) << "Try to compiled package_id: " << package_id
                << " package_name: " << package_name
                << " activity_name: " << activity_name
+               << " version: " << version
                << " file_path: " << file_path
                << " verbose: " << params.verbose
                << " perfetto_traces: "
@@ -268,12 +270,16 @@
 // Compiled the perfetto traces for activities in an package.
 bool CompilePackage(const db::DbHandle& db,
                     const std::string& package_name,
+                    int version,
                     const ControllerParameters& params) {
   std::optional<db::PackageModel> package =
-      db::PackageModel::SelectByName(db, package_name.c_str());
+        db::PackageModel::SelectByNameAndVersion(db, package_name.c_str(), version);
 
   if (!package) {
-    LOG(ERROR) << "Cannot find package for package_name: " << package_name;
+    LOG(ERROR) << "Cannot find package for package_name: "
+               << package_name
+               << " and version "
+               << version;
     return false;
   }
 
@@ -282,7 +288,7 @@
 
   bool ret = true;
   for (db::ActivityModel activity : activities) {
-    if (!CompileActivity(db, package->id, package->name, activity.name, params)) {
+    if (!CompileActivity(db, package->id, package->name, activity.name, version, params)) {
       ret = false;
     }
   }
@@ -294,7 +300,7 @@
   std::vector<db::PackageModel> packages = db::PackageModel::SelectAll(db);
   bool ret = true;
   for (db::PackageModel package : packages) {
-    if (!CompilePackage(db, package.name, params)) {
+    if (!CompilePackage(db, package.name, package.version, params)) {
       ret = false;
     }
   }
@@ -310,26 +316,32 @@
 
 bool Compile(const std::string& db_path,
              const std::string& package_name,
+             int version,
              const ControllerParameters& params) {
   iorap::db::SchemaModel db_schema = db::SchemaModel::GetOrCreate(db_path);
   db::DbHandle db{db_schema.db()};
-  return CompilePackage(db, package_name, params);
+  return CompilePackage(db, package_name, version, params);
 }
 
 bool Compile(const std::string& db_path,
              const std::string& package_name,
              const std::string& activity_name,
+             int version,
              const ControllerParameters& params) {
   iorap::db::SchemaModel db_schema = db::SchemaModel::GetOrCreate(db_path);
   db::DbHandle db{db_schema.db()};
 
   std::optional<db::PackageModel> package =
-      db::PackageModel::SelectByName(db, package_name.c_str());
+      db::PackageModel::SelectByNameAndVersion(db, package_name.c_str(), version);
+
   if (!package) {
-    LOG(ERROR) << "Cannot find package with name " << package_name;
+    LOG(ERROR) << "Cannot find package with name "
+               << package_name
+               << " and version "
+               << version;
     return false;
   }
-  return CompileActivity(db, package->id, package_name, activity_name, params);
+  return CompileActivity(db, package->id, package_name, activity_name, version, params);
 }
 
 }  // namespace iorap::maintenance
diff --git a/src/maintenance/controller.h b/src/maintenance/controller.h
index c676a52..27e40bb 100644
--- a/src/maintenance/controller.h
+++ b/src/maintenance/controller.h
@@ -82,14 +82,18 @@
 bool Compile(const std::string& db_path, const ControllerParameters& params);
 
 // Compile all activities in the package.
+// If the version is not given, an arbitrary package that has the same name is used.
 bool Compile(const std::string& db_path,
              const std::string& package_name,
+             int version,
              const ControllerParameters& params);
 
 // Compile trace for the activity.
+// If the version is not given, an arbitrary package has the same name is used.
 bool Compile(const std::string& db_path,
              const std::string& package_name,
              const std::string& activity_name,
+             int version,
              const ControllerParameters& params);
 // Visible for testing.
 bool CompileAppsOnDevice(const db::DbHandle& db, const ControllerParameters& params);
diff --git a/src/maintenance/db_cleaner.cc b/src/maintenance/db_cleaner.cc
new file mode 100644
index 0000000..8099dde
--- /dev/null
+++ b/src/maintenance/db_cleaner.cc
@@ -0,0 +1,61 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "maintenance/db_cleaner.h"
+
+#include <android-base/file.h>
+
+#include <cstdio>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <limits>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "db/clean_up.h"
+#include "db/file_models.h"
+#include "db/models.h"
+
+namespace iorap::maintenance {
+
+// Enable foreign key restriction.
+const constexpr char* kForeignKeyOnSql = "PRAGMA foreign_keys = ON;";
+
+void CleanUpDatabase(const db::DbHandle& db,
+                     std::shared_ptr<binder::PackageVersionMap> version_map) {
+  std::vector<db::PackageModel> packages = db::PackageModel::SelectAll(db);
+  // Enable cascade deletion.
+  if (!db::DbQueryBuilder::ExecuteOnce(db, kForeignKeyOnSql)) {
+    LOG(ERROR) << "Fail to turn on foreign key restraint!";
+  }
+
+  for (db::PackageModel package : packages) {
+    // Package is cleanup if it
+    // * has a null version, because each package should have version now
+    // * is not in the version map, it may be uninstalled
+    // * has an different version with the latest one
+    std::optional<int64_t> version = version_map->Find(package.name);
+    if (!version || *version != package.version) {
+      db::CleanUpFilesForPackage(db, package.id, package.name, package.version);
+      if (!package.Delete()) {
+        LOG(ERROR) << "Fail to delete package " << package.name
+                   << " with version " << package.version;
+      }
+    }
+  }
+}
+
+}  // namespace iorap::maintenance
diff --git a/src/maintenance/db_cleaner.h b/src/maintenance/db_cleaner.h
new file mode 100644
index 0000000..1168bd5
--- /dev/null
+++ b/src/maintenance/db_cleaner.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef IORAP_SRC_MAINTENANCE_VERSION_UPDATE_H_
+#define IORAP_SRC_MAINTENANCE_VERSION_UPDATE_H_
+
+#include <android/content/pm/IPackageManagerNative.h>
+
+#include <string>
+#include <vector>
+
+#include "binder/package_version_map.h"
+#include "db/file_models.h"
+
+namespace iorap::maintenance {
+
+// Clean up the database.
+// Remove all relevant data for old-version packages.
+void CleanUpDatabase(const db::DbHandle& db,
+                     std::shared_ptr<binder::PackageVersionMap> version_map);
+}  // namespace iorap::maintenance
+
+#endif  // IORAP_SRC_MAINTENANCE_VERSION_UPDATE_H_
diff --git a/src/maintenance/main.cc b/src/maintenance/main.cc
index a932886..18a0f1e 100644
--- a/src/maintenance/main.cc
+++ b/src/maintenance/main.cc
@@ -35,6 +35,7 @@
   std::cerr << "" << std::endl;
   std::cerr << "  Optional flags:" << std::endl;
   std::cerr << "    --package $,-p $           Package name." << std::endl;
+  std::cerr << "    --version $,-ve $          Package version." << std::endl;
   std::cerr << "    --activity $,-a $          Activity name." << std::endl;
   std::cerr << "    --inode-textcache $,-it $  Resolve inode->filename from textcache." << std::endl;
   std::cerr << "    --help,-h                  Print this Usage." << std::endl;
@@ -59,6 +60,7 @@
 
   std::vector<std::string> arg_input_filenames;
   std::optional<std::string> arg_package;
+  int arg_version = -1;
   std::optional<std::string> arg_activity;
   std::optional<std::string> arg_inode_textcache;
   bool recompile = false;
@@ -80,6 +82,18 @@
       }
       arg_package = arg_next;
       ++arg;
+    } else if (argstr == "--version" || argstr == "-ve") {
+      if (!has_arg_next) {
+        std::cerr << "Missing --version <value>" << std::endl;
+        return 1;
+      }
+      int version;
+      if (!android::base::ParseInt<int>(arg_next, &version)) {
+        std::cerr << "Invalid --version " << arg_next << std::endl;
+        return 1;
+      }
+      arg_version = version;
+      ++arg;
     } else if (argstr == "--activity" || argstr == "-a") {
       if (!has_arg_next) {
         std::cerr << "Missing --activity <value>" << std::endl;
@@ -144,9 +158,10 @@
     ret_code = !Compile(std::move(db),
                         std::move(*arg_package),
                         std::move(*arg_activity),
+                        arg_version,
                         params);
   } else if (arg_package) {
-    ret_code = !Compile(std::move(db), std::move(*arg_package), params);
+    ret_code = !Compile(std::move(db), std::move(*arg_package), arg_version, params);
   } else {
     ret_code = !Compile(std::move(db), params);
   }
diff --git a/src/manager/event_manager.cc b/src/manager/event_manager.cc
index b3655a5..e166a30 100644
--- a/src/manager/event_manager.cc
+++ b/src/manager/event_manager.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "binder/package_version_map.h"
 #include "common/debug.h"
 #include "common/expected.h"
 #include "common/rx_async.h"
@@ -21,6 +22,7 @@
 #include "db/file_models.h"
 #include "db/models.h"
 #include "maintenance/controller.h"
+#include "maintenance/db_cleaner.h"
 #include "manager/event_manager.h"
 #include "perfetto/rx_producer.h"
 #include "prefetcher/read_ahead.h"
@@ -35,6 +37,8 @@
 #include <atomic>
 #include <filesystem>
 #include <functional>
+#include <type_traits>
+#include <unordered_map>
 #include <utils/Trace.h>
 
 using rxcpp::observe_on_one_worker;
@@ -89,6 +93,8 @@
   std::vector<std::string> packages_;
 };
 
+using PackageVersionMap = std::unordered_map<std::string, int64_t>;
+
 // Main logic of the #OnAppLaunchEvent scan method.
 //
 // All functions are called from the same thread as the event manager
@@ -135,13 +141,16 @@
   borrowed<observe_on_one_worker*> io_thread_;  // not null
   borrowed<AsyncPool*> async_pool_;  // not null
 
+  std::shared_ptr<binder::PackageVersionMap> version_map_;
+
   explicit AppLaunchEventState(borrowed<perfetto::RxProducerFactory*> perfetto_factory,
                                bool allowed_readahead,
                                bool allowed_tracing,
                                PackageBlacklister package_blacklister,
                                borrowed<observe_on_one_worker*> thread,
                                borrowed<observe_on_one_worker*> io_thread,
-                               borrowed<AsyncPool*> async_pool)
+                               borrowed<AsyncPool*> async_pool,
+                               std::shared_ptr<binder::PackageVersionMap> version_map)
     : read_ahead_{std::make_shared<prefetcher::ReadAhead>()}
   {
     perfetto_factory_ = perfetto_factory;
@@ -160,6 +169,9 @@
 
     async_pool_ = async_pool;
     DCHECK(async_pool_ != nullptr);
+
+    version_map_ = version_map;
+    DCHECK(version_map_ != nullptr);
   }
 
   // Updates the values in this struct only as a side effect.
@@ -359,10 +371,12 @@
     // Firstly, try to find the compiled trace from sqlite.
     android::base::Timer timer{};
     db::DbHandle db{db::SchemaModel::GetSingleton()};
+    int version = version_map_->GetOrQueryPackageVersion(component_name.package);
+    db::VersionedComponentName vcn{component_name.package,
+                                   component_name.activity_name,
+                                   version};
     std::optional<db::PrefetchFileModel> compiled_trace =
-        db::PrefetchFileModel::SelectByPackageNameActivityName(db,
-                                                               component_name.package,
-                                                               component_name.activity_name);
+          db::PrefetchFileModel::SelectByVersionedComponentName(db, vcn);
 
     std::chrono::milliseconds duration_ms = timer.duration();
     LOG(DEBUG) << "EventManager: Looking up compiled trace done in "
@@ -471,9 +485,10 @@
              LOG(VERBOSE) << "StartTracing -- PerfettoTraceProto received (2)";
            });
 
+    int version = version_map_->GetOrQueryPackageVersion(component_name_->package);
     db::VersionedComponentName versioned_component_name{component_name.package,
                                                         component_name.activity_name,
-                                                        /*version*/std::nullopt};  // TODO: version
+                                                        version};
     lifetime = RxAsync::SubscribeAsync(*async_pool_,
         std::move(stream_via_threads),
         /*on_next*/[versioned_component_name]
@@ -604,10 +619,11 @@
 
     using namespace iorap::db;
 
+    int version = version_map_->GetOrQueryPackageVersion(component_name_->package);
     std::optional<ActivityModel> activity =
         ActivityModel::SelectOrInsert(db,
                                       component_name_->package,
-                                      /*version*/std::nullopt,
+                                      version,
                                       component_name_->activity_name);
 
     if (!activity) {
@@ -795,6 +811,14 @@
       worker_thread2_(rxcpp::observe_on_new_thread()),
       io_thread_(perfetto::ObserveOnNewIoThread()) {
 
+    android::base::Timer timer{};
+    version_map_ = binder::PackageVersionMap::Create();
+    std::chrono::milliseconds duration_ms = timer.duration();
+    LOG(ERROR) << " Got versions for "
+               << version_map_->Size()
+               <<" packages in "
+               << duration_ms.count() << "ms";
+
     // TODO: read all properties from one config class.
     // PH properties do not work if they contain ".". "_" was instead used here.
     const char* ph_namespace = "runtime_native_boot";
@@ -877,7 +901,8 @@
                                       package_blacklister_,
                                       &worker_thread2_,
                                       &io_thread_,
-                                      &async_pool_};
+                                      &async_pool_,
+                                      version_map_};
     app_launch_events_
       .subscribe_on(worker_thread_)
       .scan(std::move(initial_state),
@@ -900,7 +925,12 @@
                         bool verbose,
                         bool recompile,
                         uint64_t min_traces) {
+    // Update the version map.
+    version_map_->Update();
+    // Cleanup the obsolete data in the database.
     db::DbHandle db{db::SchemaModel::GetSingleton()};
+    maintenance::CleanUpDatabase(db, version_map_);
+    // Compilation
     maintenance::ControllerParameters params{
       output_text,
       inode_textcache,
@@ -1028,6 +1058,9 @@
   rxcpp::composite_subscription rx_lifetime_;  // app launch events
   rxcpp::composite_subscription rx_lifetime_jobs_;  // job scheduled events
 
+  // package version map
+  std::shared_ptr<binder::PackageVersionMap> version_map_;
+
 //INTENTIONAL_COMPILER_ERROR_HERE:
   // FIXME:
   // ok so we want to expose a 'BlockingSubscribe' or a 'Subscribe' or some kind of function