aconfig: create c++ interlop from rust flag apis

Using cxx to generate c++ code for the apis that are marked to exported
in the ffi mod in src/lib.rs. For generated code simplicity, it returns
a new flag struct for each rust api.

Create a cc_library to wrap around generated c++ code. See the created
aconfig_storage.hpp for the exported header. Note there is a nested
aconfig_storage::test_only_api namespace, where we place test only apis.
The production apis are placed under aconfig_storage namespace.

Bug: 321077378
Test: m libaconfig_storage_cc
Change-Id: I73a85a26d3749908abc461362f5e0bfc3f85bf4d
diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp
index 8c1e2cb..ff284e6 100644
--- a/tools/aconfig/aconfig_storage_file/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -89,3 +89,52 @@
     source_stem: "aconfig_storage_protos",
     host_supported: true,
 }
+
+cc_library_static {
+    name: "libaconfig_storage_protos_cc",
+    proto: {
+        export_proto_headers: true,
+        type: "lite",
+    },
+    srcs: ["protos/aconfig_storage_metadata.proto"],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    host_supported: true,
+}
+
+genrule {
+    name: "libcxx_aconfig_storage_bridge_code",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) > $(out)",
+    srcs: ["src/lib.rs"],
+    out: ["aconfig_storage/lib.rs.cc"],
+}
+
+genrule {
+    name: "libcxx_aconfig_storage_bridge_header",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) --header > $(out)",
+    srcs: ["src/lib.rs"],
+    out: ["aconfig_storage/lib.rs.h"],
+}
+
+rust_ffi_static {
+    name: "libaconfig_storage_cxx_bridge",
+    crate_name: "aconfig_storage_cxx_bridge",
+    host_supported: true,
+    defaults: ["aconfig_storage_file.defaults"],
+}
+
+cc_library_static {
+    name: "libaconfig_storage_cc",
+    srcs: ["aconfig_storage.cpp"],
+    generated_headers: [
+        "cxx-bridge-header",
+        "libcxx_aconfig_storage_bridge_header"
+    ],
+    generated_sources: ["libcxx_aconfig_storage_bridge_code"],
+    whole_static_libs: ["libaconfig_storage_cxx_bridge"],
+    export_include_dirs: ["include"],
+}
diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml
index 296d068..c4e2670 100644
--- a/tools/aconfig/aconfig_storage_file/Cargo.toml
+++ b/tools/aconfig/aconfig_storage_file/Cargo.toml
@@ -18,3 +18,4 @@
 
 [build-dependencies]
 protobuf-codegen = "3.2.0"
+cxx-build = "1.0"
diff --git a/tools/aconfig/aconfig_storage_file/aconfig_storage.cpp b/tools/aconfig/aconfig_storage_file/aconfig_storage.cpp
new file mode 100644
index 0000000..ac64093
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/aconfig_storage.cpp
@@ -0,0 +1,106 @@
+#include "aconfig_storage/aconfig_storage.hpp"
+
+#include "rust/cxx.h"
+#include "aconfig_storage/lib.rs.h"
+
+namespace aconfig_storage {
+
+/// Get package offset
+PackageOffsetQuery get_package_offset(
+    std::string const& container,
+    std::string const& package) {
+  auto offset_cxx =  get_package_offset_cxx(
+      rust::Str(container.c_str()),
+      rust::Str(package.c_str()));
+  auto offset = PackageOffsetQuery();
+  offset.query_success = offset_cxx.query_success;
+  offset.error_message = std::string(offset_cxx.error_message.c_str());
+  offset.package_exists = offset_cxx.package_exists;
+  offset.package_id = offset_cxx.package_id;
+  offset.boolean_offset = offset_cxx.boolean_offset;
+  return offset;
+}
+
+/// Get flag offset
+FlagOffsetQuery get_flag_offset(
+    std::string const& container,
+    uint32_t package_id,
+    std::string const& flag_name) {
+  auto offset_cxx =  get_flag_offset_cxx(
+      rust::Str(container.c_str()),
+      package_id,
+      rust::Str(flag_name.c_str()));
+  auto offset = FlagOffsetQuery();
+  offset.query_success = offset_cxx.query_success;
+  offset.error_message = std::string(offset_cxx.error_message.c_str());
+  offset.flag_exists = offset_cxx.flag_exists;
+  offset.flag_offset = offset_cxx.flag_offset;
+  return offset;
+}
+
+/// Get boolean flag value
+BooleanFlagValueQuery get_boolean_flag_value(
+    std::string const& container,
+    uint32_t offset) {
+  auto value_cxx =  get_boolean_flag_value_cxx(
+      rust::Str(container.c_str()),
+      offset);
+  auto value = BooleanFlagValueQuery();
+  value.query_success = value_cxx.query_success;
+  value.error_message = std::string(value_cxx.error_message.c_str());
+  value.flag_value = value_cxx.flag_value;
+  return value;
+}
+
+namespace test_only_api {
+PackageOffsetQuery get_package_offset_impl(
+    std::string const& pb_file,
+    std::string const& container,
+    std::string const& package) {
+  auto offset_cxx =  get_package_offset_cxx_impl(
+      rust::Str(pb_file.c_str()),
+      rust::Str(container.c_str()),
+      rust::Str(package.c_str()));
+  auto offset = PackageOffsetQuery();
+  offset.query_success = offset_cxx.query_success;
+  offset.error_message = std::string(offset_cxx.error_message.c_str());
+  offset.package_exists = offset_cxx.package_exists;
+  offset.package_id = offset_cxx.package_id;
+  offset.boolean_offset = offset_cxx.boolean_offset;
+  return offset;
+}
+
+FlagOffsetQuery get_flag_offset_impl(
+    std::string const& pb_file,
+    std::string const& container,
+    uint32_t package_id,
+    std::string const& flag_name) {
+  auto offset_cxx =  get_flag_offset_cxx_impl(
+      rust::Str(pb_file.c_str()),
+      rust::Str(container.c_str()),
+      package_id,
+      rust::Str(flag_name.c_str()));
+  auto offset = FlagOffsetQuery();
+  offset.query_success = offset_cxx.query_success;
+  offset.error_message = std::string(offset_cxx.error_message.c_str());
+  offset.flag_exists = offset_cxx.flag_exists;
+  offset.flag_offset = offset_cxx.flag_offset;
+  return offset;
+}
+
+BooleanFlagValueQuery get_boolean_flag_value_impl(
+    std::string const& pb_file,
+    std::string const& container,
+    uint32_t offset) {
+  auto value_cxx =  get_boolean_flag_value_cxx_impl(
+      rust::Str(pb_file.c_str()),
+      rust::Str(container.c_str()),
+      offset);
+  auto value = BooleanFlagValueQuery();
+  value.query_success = value_cxx.query_success;
+  value.error_message = std::string(value_cxx.error_message.c_str());
+  value.flag_value = value_cxx.flag_value;
+  return value;
+}
+} // namespace test_only_api
+} // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_file/build.rs b/tools/aconfig/aconfig_storage_file/build.rs
index 1feeb60..894b71c 100644
--- a/tools/aconfig/aconfig_storage_file/build.rs
+++ b/tools/aconfig/aconfig_storage_file/build.rs
@@ -14,4 +14,7 @@
         .inputs(proto_files)
         .cargo_out_dir("aconfig_storage_protos")
         .run_from_script();
+
+    let _ = cxx_build::bridge("src/lib.rs");
+    println!("cargo:rerun-if-changed=src/lib.rs");
 }
diff --git a/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage.hpp b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage.hpp
new file mode 100644
index 0000000..636fb7e
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage.hpp
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+namespace aconfig_storage {
+
+/// Package offset query result
+struct PackageOffsetQuery {
+  bool query_success;
+  std::string error_message;
+  bool package_exists;
+  uint32_t package_id;
+  uint32_t boolean_offset;
+};
+
+/// Flag offset query result
+struct FlagOffsetQuery {
+  bool query_success;
+  std::string error_message;
+  bool flag_exists;
+  uint16_t flag_offset;
+};
+
+/// Boolean flag value query result
+struct BooleanFlagValueQuery {
+  bool query_success;
+  std::string error_message;
+  bool flag_value;
+};
+
+/// Get package offset
+/// \input container: the flag container name
+/// \input package: the flag package name
+/// \returns a PackageOffsetQuery
+PackageOffsetQuery get_package_offset(
+    std::string const& container,
+    std::string const& package);
+
+/// Get flag offset
+/// \input container: the flag container name
+/// \input package_id: the flag package id obtained from package offset query
+/// \input flag_name: flag name
+/// \returns a FlagOffsetQuery
+FlagOffsetQuery get_flag_offset(
+    std::string const& container,
+    uint32_t package_id,
+    std::string const& flag_name);
+
+/// Get boolean flag value
+/// \input container: the flag container name
+/// \input offset: the boolean flag value byte offset in the file
+/// \returns a BooleanFlagValueQuery
+BooleanFlagValueQuery get_boolean_flag_value(
+    std::string const& container,
+    uint32_t offset);
+
+/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE, TEST ONLY
+namespace test_only_api {
+PackageOffsetQuery get_package_offset_impl(
+    std::string const& pb_file,
+    std::string const& container,
+    std::string const& package);
+
+FlagOffsetQuery get_flag_offset_impl(
+    std::string const& pb_file,
+    std::string const& container,
+    uint32_t package_id,
+    std::string const& flag_name);
+
+BooleanFlagValueQuery get_boolean_flag_value_impl(
+    std::string const& pb_file,
+    std::string const& container,
+    uint32_t offset);
+} // namespace test_only_api
+} // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index e87207a..c238f24 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -256,6 +256,185 @@
     get_boolean_flag_value_impl(STORAGE_LOCATION_FILE, container, offset)
 }
 
+#[cxx::bridge]
+mod ffi {
+    // Package table query return for cc interlop
+    pub struct PackageOffsetQueryCXX {
+        pub query_success: bool,
+        pub error_message: String,
+        pub package_exists: bool,
+        pub package_id: u32,
+        pub boolean_offset: u32,
+    }
+
+    // Flag table query return for cc interlop
+    pub struct FlagOffsetQueryCXX {
+        pub query_success: bool,
+        pub error_message: String,
+        pub flag_exists: bool,
+        pub flag_offset: u16,
+    }
+
+    // Flag value query return for cc interlop
+    pub struct BooleanFlagValueQueryCXX {
+        pub query_success: bool,
+        pub error_message: String,
+        pub flag_value: bool,
+    }
+
+    // Rust export to c++
+    extern "Rust" {
+        pub fn get_package_offset_cxx_impl(
+            pb_file: &str,
+            container: &str,
+            package: &str,
+        ) -> PackageOffsetQueryCXX;
+
+        pub fn get_flag_offset_cxx_impl(
+            pb_file: &str,
+            container: &str,
+            package_id: u32,
+            flag: &str,
+        ) -> FlagOffsetQueryCXX;
+
+        pub fn get_boolean_flag_value_cxx_impl(
+            pb_file: &str,
+            container: &str,
+            offset: u32,
+        ) -> BooleanFlagValueQueryCXX;
+
+        pub fn get_package_offset_cxx(container: &str, package: &str) -> PackageOffsetQueryCXX;
+
+        pub fn get_flag_offset_cxx(
+            container: &str,
+            package_id: u32,
+            flag: &str,
+        ) -> FlagOffsetQueryCXX;
+
+        pub fn get_boolean_flag_value_cxx(container: &str, offset: u32)
+            -> BooleanFlagValueQueryCXX;
+    }
+}
+
+/// Get package start offset impl cc interlop
+pub fn get_package_offset_cxx_impl(
+    pb_file: &str,
+    container: &str,
+    package: &str,
+) -> ffi::PackageOffsetQueryCXX {
+    ffi::PackageOffsetQueryCXX::new(get_package_offset_impl(pb_file, container, package))
+}
+
+/// Get flag start offset impl cc interlop
+pub fn get_flag_offset_cxx_impl(
+    pb_file: &str,
+    container: &str,
+    package_id: u32,
+    flag: &str,
+) -> ffi::FlagOffsetQueryCXX {
+    ffi::FlagOffsetQueryCXX::new(get_flag_offset_impl(pb_file, container, package_id, flag))
+}
+
+/// Get boolean flag value impl cc interlop
+pub fn get_boolean_flag_value_cxx_impl(
+    pb_file: &str,
+    container: &str,
+    offset: u32,
+) -> ffi::BooleanFlagValueQueryCXX {
+    ffi::BooleanFlagValueQueryCXX::new(get_boolean_flag_value_impl(pb_file, container, offset))
+}
+
+/// Get package start offset cc interlop
+pub fn get_package_offset_cxx(container: &str, package: &str) -> ffi::PackageOffsetQueryCXX {
+    ffi::PackageOffsetQueryCXX::new(get_package_offset(container, package))
+}
+
+/// Get flag start offset cc interlop
+pub fn get_flag_offset_cxx(
+    container: &str,
+    package_id: u32,
+    flag: &str,
+) -> ffi::FlagOffsetQueryCXX {
+    ffi::FlagOffsetQueryCXX::new(get_flag_offset(container, package_id, flag))
+}
+
+/// Get boolean flag value cc interlop
+pub fn get_boolean_flag_value_cxx(container: &str, offset: u32) -> ffi::BooleanFlagValueQueryCXX {
+    ffi::BooleanFlagValueQueryCXX::new(get_boolean_flag_value(container, offset))
+}
+
+impl ffi::PackageOffsetQueryCXX {
+    pub(crate) fn new(offset_result: Result<Option<PackageOffset>, AconfigStorageError>) -> Self {
+        match offset_result {
+            Ok(offset_opt) => match offset_opt {
+                Some(offset) => Self {
+                    query_success: true,
+                    error_message: String::from(""),
+                    package_exists: true,
+                    package_id: offset.package_id,
+                    boolean_offset: offset.boolean_offset,
+                },
+                None => Self {
+                    query_success: true,
+                    error_message: String::from(""),
+                    package_exists: false,
+                    package_id: 0,
+                    boolean_offset: 0,
+                },
+            },
+            Err(errmsg) => Self {
+                query_success: false,
+                error_message: format!("{:?}", errmsg),
+                package_exists: false,
+                package_id: 0,
+                boolean_offset: 0,
+            },
+        }
+    }
+}
+
+impl ffi::FlagOffsetQueryCXX {
+    pub(crate) fn new(offset_result: Result<Option<FlagOffset>, AconfigStorageError>) -> Self {
+        match offset_result {
+            Ok(offset_opt) => match offset_opt {
+                Some(offset) => Self {
+                    query_success: true,
+                    error_message: String::from(""),
+                    flag_exists: true,
+                    flag_offset: offset,
+                },
+                None => Self {
+                    query_success: true,
+                    error_message: String::from(""),
+                    flag_exists: false,
+                    flag_offset: 0,
+                },
+            },
+            Err(errmsg) => Self {
+                query_success: false,
+                error_message: format!("{:?}", errmsg),
+                flag_exists: false,
+                flag_offset: 0,
+            },
+        }
+    }
+}
+
+impl ffi::BooleanFlagValueQueryCXX {
+    pub(crate) fn new(value_result: Result<bool, AconfigStorageError>) -> Self {
+        match value_result {
+            Ok(value) => {
+                Self { query_success: true, error_message: String::from(""), flag_value: value }
+            }
+            Err(errmsg) => Self {
+                query_success: false,
+                error_message: format!("{:?}", errmsg),
+                flag_value: false,
+            },
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;