[installd] Create profile snaphots for boot image

Create the profile snapshot for the boot image by aggregating all primary
profiles. During aggregation data that does not belong to the boot image
is filtered out. The matching is done based on the dex files provided in
the classpath argument.

Test: installd_dexopt_test
Bug: 30934496
Change-Id: Ib980ab3feb9f9838dff81a3861693cd08b1df9ab
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index f3d9459..a8c32ed 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -28,6 +28,7 @@
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 #include <cutils/fs.h>
 #include <cutils/properties.h>
 #include <log/log.h>
@@ -41,7 +42,9 @@
 
 #define DEBUG_XATTRS 0
 
+using android::base::EndsWith;
 using android::base::StringPrintf;
+using android::base::unique_fd;
 
 namespace android {
 namespace installd {
@@ -985,5 +988,75 @@
     return 0;
 }
 
+// Collect all non empty profiles from the given directory and puts then into profile_paths.
+// The profiles are identified based on PROFILE_EXT extension.
+// If a subdirectory or profile file cannot be opened the method logs a warning and moves on.
+// It returns true if there were no errors at all, and false otherwise.
+static bool collect_profiles(DIR* d,
+                             const std::string& current_path,
+                             std::vector<std::string>* profiles_paths) {
+    int32_t dir_fd = dirfd(d);
+    if (dir_fd < 0) {
+        return false;
+    }
+
+    bool result = true;
+    struct dirent* dir_entry;
+    while ((dir_entry = readdir(d))) {
+        std::string name = dir_entry->d_name;
+        std::string local_path = current_path + "/" + name;
+
+        if (dir_entry->d_type == DT_REG) {
+            // Check if this is a non empty profile file.
+            if (EndsWith(name, PROFILE_EXT)) {
+                struct stat st;
+                if (stat(local_path.c_str(), &st) != 0) {
+                    PLOG(WARNING) << "Cannot stat local path " << local_path;
+                    result = false;
+                    continue;
+                } else if (st.st_size > 0) {
+                    profiles_paths->push_back(local_path);
+                }
+            }
+        } else if (dir_entry->d_type == DT_DIR) {
+            // always skip "." and ".."
+            if (name == "." || name == "..") {
+                continue;
+            }
+
+            unique_fd subdir_fd(openat(dir_fd, name.c_str(),
+                    O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC));
+            if (subdir_fd < 0) {
+                PLOG(WARNING) << "Could not open dir path " << local_path;
+                result = false;
+                continue;
+            }
+
+            DIR* subdir = fdopendir(subdir_fd);
+            if (subdir == NULL) {
+                PLOG(WARNING) << "Could not open dir path " << local_path;
+                result = false;
+                continue;
+            }
+            bool new_result = collect_profiles(subdir, local_path, profiles_paths);
+            result = result && new_result;
+            if (closedir(subdir) != 0) {
+                PLOG(WARNING) << "Could not close dir path " << local_path;
+            }
+        }
+    }
+
+    return result;
+}
+
+bool collect_profiles(std::vector<std::string>* profiles_paths) {
+    DIR* d = opendir(android_profiles_dir.c_str());
+    if (d == NULL) {
+        return false;
+    } else {
+        return collect_profiles(d, android_profiles_dir, profiles_paths);
+    }
+}
+
 }  // namespace installd
 }  // namespace android