Scan Active APEXes
am: 86b57f60c6

Change-Id: Ia8d73abd04d846c42148b9b3711129e034b19814
diff --git a/Android.bp b/Android.bp
index 3f3b84f..61400af 100644
--- a/Android.bp
+++ b/Android.bp
@@ -21,11 +21,11 @@
         "-Werror",
         "-Wextra",
     ],
+    defaults: ["libapexutil-deps"],
     static_libs: [
+        "libapexutil",
         "libbase",
         "liblog",
-        "libprotobuf-cpp-lite",
-        "lib_apex_manifest_proto_lite",
     ],
     host_supported: true,
 }
diff --git a/modules/apex.cc b/modules/apex.cc
new file mode 100644
index 0000000..6c71073
--- /dev/null
+++ b/modules/apex.cc
@@ -0,0 +1,50 @@
+/*
+ * 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 "linkerconfig/apex.h"
+
+#include <unistd.h>
+
+#include <apexutil.h>
+
+namespace {
+bool DirExists(const std::string& path) {
+  return access(path.c_str(), F_OK) == 0;
+}
+}  // namespace
+
+namespace android {
+namespace linkerconfig {
+namespace modules {
+std::map<std::string, ApexInfo> ScanActiveApexes(const std::string& apex_root) {
+  std::map<std::string, ApexInfo> apexes;
+  for (const auto& [path, manifest] : apex::GetActivePackages(apex_root)) {
+    bool has_bin = DirExists(path + "/bin");
+    bool has_lib = DirExists(path + "/lib") || DirExists(path + "/lib64");
+    ApexInfo info(manifest.name(),
+                  path,
+                  {manifest.providenativelibs().begin(),
+                   manifest.providenativelibs().end()},
+                  {manifest.requirenativelibs().begin(),
+                   manifest.requirenativelibs().end()},
+                  has_bin,
+                  has_lib);
+    apexes.emplace(manifest.name(), std::move(info));
+  }
+  return apexes;
+}
+}  // namespace modules
+}  // namespace linkerconfig
+}  // namespace android
\ No newline at end of file
diff --git a/modules/include/linkerconfig/apex.h b/modules/include/linkerconfig/apex.h
new file mode 100644
index 0000000..8b0838e
--- /dev/null
+++ b/modules/include/linkerconfig/apex.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace linkerconfig {
+namespace modules {
+struct ApexInfo {
+  std::string name;
+  std::string path;
+  std::vector<std::string> provide_libs;
+  std::vector<std::string> require_libs;
+  bool has_bin;
+  bool has_lib;
+
+  ApexInfo() = default;  // for std::map::operator[]
+  ApexInfo(std::string name, std::string path,
+           std::vector<std::string> provide_libs,
+           std::vector<std::string> require_libs, bool has_bin, bool has_lib)
+      : name(std::move(name)),
+        path(std::move(path)),
+        provide_libs(std::move(provide_libs)),
+        require_libs(std::move(require_libs)),
+        has_bin(has_bin),
+        has_lib(has_lib) {
+  }
+};
+
+std::map<std::string, ApexInfo> ScanActiveApexes(const std::string& apex_root);
+}  // namespace modules
+}  // namespace linkerconfig
+}  // namespace android
\ No newline at end of file
diff --git a/modules/include/linkerconfig/namespace.h b/modules/include/linkerconfig/namespace.h
index 1cc6a6b..ca92b14 100644
--- a/modules/include/linkerconfig/namespace.h
+++ b/modules/include/linkerconfig/namespace.h
@@ -19,8 +19,7 @@
 #include <string>
 #include <vector>
 
-#include <android-base/result.h>
-
+#include "linkerconfig/apex.h"
 #include "linkerconfig/configwriter.h"
 #include "linkerconfig/link.h"
 #include "linkerconfig/log.h"
@@ -143,9 +142,7 @@
                        const std::vector<std::string>& path_list);
 };
 
-::android::base::Result<void> InitializeWithApex(Namespace& ns,
-                                                 const std::string& apex_path);
-
+void InitializeWithApex(Namespace& ns, const ApexInfo& apex_info);
 }  // namespace modules
 }  // namespace linkerconfig
 }  // namespace android
diff --git a/modules/namespace.cc b/modules/namespace.cc
index bdc45d5..ae0946b 100644
--- a/modules/namespace.cc
+++ b/modules/namespace.cc
@@ -16,18 +16,11 @@
 
 #include "linkerconfig/namespace.h"
 
-#include <android-base/file.h>
 #include <android-base/strings.h>
-#include <apex_manifest.pb.h>
 
+#include "linkerconfig/apex.h"
 #include "linkerconfig/log.h"
 
-using ::android::base::Error;
-using ::android::base::ReadFileToString;
-using ::android::base::Result;
-using ::android::base::WriteStringToFile;
-using ::apex::proto::ApexManifest;
-
 namespace {
 
 constexpr const char* kDataAsanPath = "/data/asan";
@@ -43,35 +36,17 @@
   return false;
 }
 
-Result<ApexManifest> ParseApexManifest(const std::string& manifest_path) {
-  std::string content;
-  if (!ReadFileToString(manifest_path, &content)) {
-    return Error() << "Failed to read manifest file: " << manifest_path;
-  }
-
-  ApexManifest manifest;
-  if (!manifest.ParseFromString(content)) {
-    return Error() << "Can't parse APEX manifest.";
-  }
-  return manifest;
-}
-
 }  // namespace
 
 namespace android {
 namespace linkerconfig {
 namespace modules {
 
-Result<void> InitializeWithApex(Namespace& ns, const std::string& apex_path) {
-  auto apex_manifest = ParseApexManifest(apex_path + "/apex_manifest.pb");
-  if (!apex_manifest) {
-    return apex_manifest.error();
-  }
-  ns.AddSearchPath(apex_path + "/${LIB}");
+void InitializeWithApex(Namespace& ns, const ApexInfo& apex_info) {
+  ns.AddSearchPath(apex_info.path + "/${LIB}");
   ns.AddPermittedPath("/system/${LIB}");
-  ns.AddProvides(apex_manifest->providenativelibs());
-  ns.AddRequires(apex_manifest->requirenativelibs());
-  return {};
+  ns.AddProvides(apex_info.provide_libs);
+  ns.AddRequires(apex_info.require_libs);
 }
 
 void Namespace::WritePathString(ConfigWriter& writer,
diff --git a/modules/tests/apex_test.cc b/modules/tests/apex_test.cc
index 72923a5..301e370 100644
--- a/modules/tests/apex_test.cc
+++ b/modules/tests/apex_test.cc
@@ -15,81 +15,104 @@
  */
 
 #include <string>
-#include <utility>
 #include <vector>
 
 #include <android-base/file.h>
-#include <android-base/result.h>
-#include <android-base/stringprintf.h>
 #include <apex_manifest.pb.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "linkerconfig/apex.h"
 #include "linkerconfig/configwriter.h"
 #include "linkerconfig/namespace.h"
 #include "linkerconfig/section.h"
 
-using ::android::base::Result;
-using ::android::base::StringPrintf;
 using ::android::base::WriteStringToFile;
+using ::android::linkerconfig::modules::ApexInfo;
 using ::android::linkerconfig::modules::ConfigWriter;
 using ::android::linkerconfig::modules::InitializeWithApex;
 using ::android::linkerconfig::modules::Namespace;
+using ::android::linkerconfig::modules::ScanActiveApexes;
 using ::android::linkerconfig::modules::Section;
 using ::apex::proto::ApexManifest;
 using ::testing::Contains;
 
 namespace {
+struct ApexTest : ::testing::Test {
+  TemporaryDir tmp_dir;
+  std::string apex_root;
 
-void PrepareTestApexDir(const std::string& path, const std::string& name,
-                        std::vector<std::string> provided_libs,
-                        std::vector<std::string> required_libs) {
-  ApexManifest manifest;
-  manifest.set_name(name);
-  for (auto lib : provided_libs) {
-    manifest.add_providenativelibs(lib);
+  void SetUp() override {
+    apex_root = tmp_dir.path + std::string("/");
   }
-  for (auto lib : required_libs) {
-    manifest.add_requirenativelibs(lib);
-  }
-  std::string content = manifest.SerializeAsString();
-  std::string manifest_path = path + "/apex_manifest.pb";
-  if (!WriteStringToFile(content, manifest_path)) {
-    LOG(ERROR) << "Failed to write a file: " << manifest_path;
-  }
-}
 
+  void PrepareApex(std::string apex_name, std::vector<std::string> provided_libs,
+                   std::vector<std::string> required_libs) {
+    ApexManifest manifest;
+    manifest.set_name(apex_name);
+    for (auto lib : provided_libs) {
+      manifest.add_providenativelibs(lib);
+    }
+    for (auto lib : required_libs) {
+      manifest.add_requirenativelibs(lib);
+    }
+    WriteFile(apex_name + "/apex_manifest.pb", manifest.SerializeAsString());
+  }
+
+  void Mkdir(std::string dir_path) {
+    if (access(dir_path.c_str(), F_OK) == 0) return;
+    Mkdir(android::base::Dirname(dir_path));
+    ASSERT_NE(-1, mkdir(dir_path.c_str(), 0755) == -1)
+        << "Failed to create a directory: " << dir_path;
+  }
+
+  void WriteFile(std::string file, std::string content) {
+    std::string file_path = apex_root + file;
+    Mkdir(::android::base::Dirname(file_path));
+    ASSERT_TRUE(WriteStringToFile(content, file_path))
+        << "Failed to write a file: " << file_path;
+  }
+};
 }  // namespace
 
 TEST(apex_namespace, build_namespace) {
-  TemporaryDir foo_dir;
-  PrepareTestApexDir(foo_dir.path, "foo", {}, {});
-
   Namespace ns("foo");
-  auto result = InitializeWithApex(ns, foo_dir.path);
-  ASSERT_TRUE(result);
+  InitializeWithApex(ns,
+                     ApexInfo("com.android.foo",
+                              "/apex/com.android.foo",
+                              {},
+                              {},
+                              /*has_bin=*/false,
+                              /*has_lib=*/true));
 
   ConfigWriter writer;
   ns.WriteConfig(writer);
   ASSERT_EQ(
-      StringPrintf("namespace.foo.isolated = false\n"
-                   "namespace.foo.search.paths = %s/${LIB}\n"
-                   "namespace.foo.permitted.paths = /system/${LIB}\n"
-                   "namespace.foo.asan.search.paths = %s/${LIB}\n"
-                   "namespace.foo.asan.permitted.paths = /system/${LIB}\n",
-                   foo_dir.path,
-                   foo_dir.path),
+      "namespace.foo.isolated = false\n"
+      "namespace.foo.search.paths = /apex/com.android.foo/${LIB}\n"
+      "namespace.foo.permitted.paths = /system/${LIB}\n"
+      "namespace.foo.asan.search.paths = /apex/com.android.foo/${LIB}\n"
+      "namespace.foo.asan.permitted.paths = /system/${LIB}\n",
+
       writer.ToString());
 }
 
 TEST(apex_namespace, resolve_between_apex_namespaces) {
-  TemporaryDir foo_dir, bar_dir;
-  PrepareTestApexDir(foo_dir.path, "foo", {"foo.so"}, {"bar.so"});
-  PrepareTestApexDir(bar_dir.path, "bar", {"bar.so"}, {});
-
   Namespace foo("foo"), bar("bar");
-  InitializeWithApex(foo, foo_dir.path);
-  InitializeWithApex(bar, bar_dir.path);
+  InitializeWithApex(foo,
+                     ApexInfo("com.android.foo",
+                              "/apex/com.android.foo",
+                              {"foo.so"},
+                              {"bar.so"},
+                              /*has_bin=*/false,
+                              /*has_lib=*/true));
+  InitializeWithApex(bar,
+                     ApexInfo("com.android.bar",
+                              "/apex/com.android.bar",
+                              {"bar.so"},
+                              {},
+                              /*has_bin=*/false,
+                              /*has_lib=*/true));
 
   std::vector<Namespace> namespaces;
   namespaces.push_back(std::move(foo));
@@ -102,4 +125,22 @@
   // See if two namespaces are linked correctly
   ASSERT_THAT(section.GetNamespace("foo")->GetLink("bar").GetSharedLibs(),
               Contains("bar.so"));
+}
+
+TEST_F(ApexTest, scan_apex_dir) {
+  PrepareApex("foo", {}, {"bar.so"});
+  WriteFile("foo/bin/foo", "");
+  PrepareApex("bar", {"bar.so"}, {});
+  WriteFile("bar/lib64/bar.so", "");
+
+  auto apexes = ScanActiveApexes(apex_root);
+  ASSERT_EQ(2U, apexes.size());
+
+  ASSERT_THAT(apexes["foo"].require_libs, Contains("bar.so"));
+  ASSERT_TRUE(apexes["foo"].has_bin);
+  ASSERT_FALSE(apexes["foo"].has_lib);
+
+  ASSERT_THAT(apexes["bar"].provide_libs, Contains("bar.so"));
+  ASSERT_FALSE(apexes["bar"].has_bin);
+  ASSERT_TRUE(apexes["bar"].has_lib);
 }
\ No newline at end of file