aconfig: add a new aconfig storage file flag listing api

added a new function called list_flag_with_info to list all the flags
given all four storage files (package.map, flag.map, flag.val, flag.info).
also exported this api thru cxx interface so aconfigd can use it.

Bug: b/312444587
Test: atest -c
Change-Id: Ibbfe657b980d40e25e5e28962b930338192e2d98
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index b7ff8ef..421e94a 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -97,5 +97,9 @@
     }
   ],
   "postsubmit": [
+    {
+      // aconfig_storage file cpp integration tests
+      "name": "aconfig_storage_file.test.cpp"
+    }
   ]
 }
diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp
index 08c00b0..e066e31 100644
--- a/tools/aconfig/aconfig_storage_file/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -12,6 +12,7 @@
         "libtempfile",
         "libprotobuf",
         "libclap",
+        "libcxx",
         "libaconfig_storage_protos",
     ],
 }
@@ -77,3 +78,62 @@
     product_available: true,
     double_loadable: true,
 }
+
+// cxx source codegen from rust api
+genrule {
+    name: "libcxx_aconfig_storage_file_bridge_code",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) > $(out)",
+    srcs: ["src/lib.rs"],
+    out: ["aconfig_storage/lib.rs.cc"],
+}
+
+// cxx header codegen from rust api
+genrule {
+    name: "libcxx_aconfig_storage_file_bridge_header",
+    tools: ["cxxbridge"],
+    cmd: "$(location cxxbridge) $(in) --header > $(out)",
+    srcs: ["src/lib.rs"],
+    out: ["aconfig_storage/lib.rs.h"],
+}
+
+// a static cc lib based on generated code
+rust_ffi_static {
+    name: "libaconfig_storage_file_cxx_bridge",
+    crate_name: "aconfig_storage_file_cxx_bridge",
+    host_supported: true,
+    vendor_available: true,
+    product_available: true,
+    srcs: ["src/lib.rs"],
+    defaults: ["aconfig_storage_file.defaults"],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    min_sdk_version: "29",
+}
+
+// storage file parse api cc interface
+cc_library {
+    name: "libaconfig_storage_file_cc",
+    srcs: ["aconfig_storage_file.cpp"],
+    generated_headers: [
+        "cxx-bridge-header",
+        "libcxx_aconfig_storage_file_bridge_header",
+    ],
+    generated_sources: ["libcxx_aconfig_storage_file_bridge_code"],
+    whole_static_libs: ["libaconfig_storage_file_cxx_bridge"],
+    export_include_dirs: ["include"],
+    host_supported: true,
+    vendor_available: true,
+    product_available: true,
+    shared_libs: [
+        "libbase",
+    ],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    min_sdk_version: "29",
+    double_loadable: true,
+}
diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml
index 641f481..192dfad 100644
--- a/tools/aconfig/aconfig_storage_file/Cargo.toml
+++ b/tools/aconfig/aconfig_storage_file/Cargo.toml
@@ -13,6 +13,7 @@
 tempfile = "3.9.0"
 thiserror = "1.0.56"
 clap = { version = "4.1.8", features = ["derive"] }
+cxx = "1.0"
 
 [[bin]]
 name = "aconfig-storage"
diff --git a/tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp b/tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp
new file mode 100644
index 0000000..548078f
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp
@@ -0,0 +1,38 @@
+#include "rust/cxx.h"
+#include "aconfig_storage/lib.rs.h"
+
+#include "aconfig_storage/aconfig_storage_file.hpp"
+
+using namespace android::base;
+
+namespace aconfig_storage {
+
+Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info(
+    const std::string& package_map,
+    const std::string& flag_map,
+    const std::string& flag_val,
+    const std::string& flag_info) {
+  auto flag_list_cxx = list_flags_with_info_cxx(rust::Str(package_map.c_str()),
+                                                rust::Str(flag_map.c_str()),
+                                                rust::Str(flag_val.c_str()),
+                                                rust::Str(flag_info.c_str()));
+  if (flag_list_cxx.query_success) {
+    auto flag_list = std::vector<FlagValueAndInfoSummary>();
+    for (const auto& flag_cxx : flag_list_cxx.flags) {
+      auto flag = FlagValueAndInfoSummary();
+      flag.package_name = std::string(flag_cxx.package_name);
+      flag.flag_name = std::string(flag_cxx.flag_name);
+      flag.flag_value = std::string(flag_cxx.flag_value);
+      flag.value_type = std::string(flag_cxx.value_type);
+      flag.is_readwrite = flag_cxx.is_readwrite;
+      flag.has_server_override = flag_cxx.has_server_override;
+      flag.has_local_override = flag_cxx.has_local_override;
+      flag_list.push_back(flag);
+    }
+    return flag_list;
+  } else {
+    return Error() << flag_list_cxx.error_message;
+  }
+}
+
+} // namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_file/build.rs b/tools/aconfig/aconfig_storage_file/build.rs
index 1feeb60..e0ade2a 100644
--- a/tools/aconfig/aconfig_storage_file/build.rs
+++ b/tools/aconfig/aconfig_storage_file/build.rs
@@ -14,4 +14,6 @@
         .inputs(proto_files)
         .cargo_out_dir("aconfig_storage_protos")
         .run_from_script();
+
+    let _ = cxx_build::bridge("src/lib.rs");
 }
diff --git a/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp
new file mode 100644
index 0000000..5044a4d
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/include/aconfig_storage/aconfig_storage_file.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <android-base/result.h>
+
+namespace aconfig_storage {
+
+/// Flag value and info summary for a flag
+struct FlagValueAndInfoSummary {
+  std::string package_name;
+  std::string flag_name;
+  std::string flag_value;
+  std::string value_type;
+  bool is_readwrite;
+  bool has_server_override;
+  bool has_local_override;
+};
+
+/// List all flag values with their flag info
+/// \input package_map: package map file
+/// \input flag_map: flag map file
+/// \input flag_val: flag value file
+/// \input flag_info: flag info file
+android::base::Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info(
+    const std::string& package_map,
+    const std::string& flag_map,
+    const std::string& flag_val,
+    const std::string& flag_info);
+
+}// namespace aconfig_storage
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index 2acfc7d..80602bb 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -278,12 +278,21 @@
     Ok(buffer)
 }
 
+/// Flag value summary
+#[derive(Debug, PartialEq)]
+pub struct FlagValueSummary {
+    pub package_name: String,
+    pub flag_name: String,
+    pub flag_value: String,
+    pub value_type: StoredFlagType,
+}
+
 /// List flag values from storage files
 pub fn list_flags(
     package_map: &str,
     flag_map: &str,
     flag_val: &str,
-) -> Result<Vec<(String, String, StoredFlagType, bool)>, AconfigStorageError> {
+) -> Result<Vec<FlagValueSummary>, AconfigStorageError> {
     let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
     let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
     let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
@@ -295,30 +304,155 @@
 
     let mut flags = Vec::new();
     for node in flag_table.nodes.iter() {
-        let (package_name, package_offset) = package_info[node.package_id as usize];
-        let flag_offset = package_offset + node.flag_index as u32;
-        let flag_value = flag_value_list.booleans[flag_offset as usize];
-        flags.push((
-            String::from(package_name),
-            node.flag_name.clone(),
-            node.flag_type,
-            flag_value,
-        ));
+        let (package_name, boolean_start_index) = package_info[node.package_id as usize];
+        let flag_index = boolean_start_index + node.flag_index as u32;
+        let flag_value = flag_value_list.booleans[flag_index as usize];
+        flags.push(FlagValueSummary {
+            package_name: String::from(package_name),
+            flag_name: node.flag_name.clone(),
+            flag_value: flag_value.to_string(),
+            value_type: node.flag_type,
+        });
     }
 
-    flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) {
-        Ordering::Equal => v1.1.cmp(&v2.1),
+    flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {
+        Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),
         other => other,
     });
     Ok(flags)
 }
 
+/// Flag value and info summary
+#[derive(Debug, PartialEq)]
+pub struct FlagValueAndInfoSummary {
+    pub package_name: String,
+    pub flag_name: String,
+    pub flag_value: String,
+    pub value_type: StoredFlagType,
+    pub is_readwrite: bool,
+    pub has_server_override: bool,
+    pub has_local_override: bool,
+}
+
+/// List flag values and info from storage files
+pub fn list_flags_with_info(
+    package_map: &str,
+    flag_map: &str,
+    flag_val: &str,
+    flag_info: &str,
+) -> Result<Vec<FlagValueAndInfoSummary>, AconfigStorageError> {
+    let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
+    let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
+    let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
+    let flag_info = FlagInfoList::from_bytes(&read_file_to_bytes(flag_info)?)?;
+
+    let mut package_info = vec![("", 0); package_table.header.num_packages as usize];
+    for node in package_table.nodes.iter() {
+        package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index);
+    }
+
+    let mut flags = Vec::new();
+    for node in flag_table.nodes.iter() {
+        let (package_name, boolean_start_index) = package_info[node.package_id as usize];
+        let flag_index = boolean_start_index + node.flag_index as u32;
+        let flag_value = flag_value_list.booleans[flag_index as usize];
+        let flag_attribute = flag_info.nodes[flag_index as usize].attributes;
+        flags.push(FlagValueAndInfoSummary {
+            package_name: String::from(package_name),
+            flag_name: node.flag_name.clone(),
+            flag_value: flag_value.to_string(),
+            value_type: node.flag_type,
+            is_readwrite: flag_attribute & (FlagInfoBit::IsReadWrite as u8) != 0,
+            has_server_override: flag_attribute & (FlagInfoBit::HasServerOverride as u8) != 0,
+            has_local_override: flag_attribute & (FlagInfoBit::HasLocalOverride as u8) != 0,
+        });
+    }
+
+    flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {
+        Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),
+        other => other,
+    });
+    Ok(flags)
+}
+
+// *************************************** //
+// CC INTERLOP
+// *************************************** //
+
+// Exported rust data structure and methods, c++ code will be generated
+#[cxx::bridge]
+mod ffi {
+    /// flag value and info summary cxx return
+    pub struct FlagValueAndInfoSummaryCXX {
+        pub package_name: String,
+        pub flag_name: String,
+        pub flag_value: String,
+        pub value_type: String,
+        pub is_readwrite: bool,
+        pub has_server_override: bool,
+        pub has_local_override: bool,
+    }
+
+    /// list flag result cxx return
+    pub struct ListFlagValueAndInfoResultCXX {
+        pub query_success: bool,
+        pub error_message: String,
+        pub flags: Vec<FlagValueAndInfoSummaryCXX>,
+    }
+
+    // Rust export to c++
+    extern "Rust" {
+        pub fn list_flags_with_info_cxx(
+            package_map: &str,
+            flag_map: &str,
+            flag_val: &str,
+            flag_info: &str,
+        ) -> ListFlagValueAndInfoResultCXX;
+    }
+}
+
+/// implement flag value and info summary cxx return type
+impl ffi::FlagValueAndInfoSummaryCXX {
+    pub(crate) fn new(summary: FlagValueAndInfoSummary) -> Self {
+        Self {
+            package_name: summary.package_name,
+            flag_name: summary.flag_name,
+            flag_value: summary.flag_value,
+            value_type: format!("{:?}", summary.value_type),
+            is_readwrite: summary.is_readwrite,
+            has_server_override: summary.has_server_override,
+            has_local_override: summary.has_local_override,
+        }
+    }
+}
+
+/// implement list flag with info cxx interlop
+pub fn list_flags_with_info_cxx(
+    package_map: &str,
+    flag_map: &str,
+    flag_val: &str,
+    flag_info: &str,
+) -> ffi::ListFlagValueAndInfoResultCXX {
+    match list_flags_with_info(package_map, flag_map, flag_val, flag_info) {
+        Ok(summary) => ffi::ListFlagValueAndInfoResultCXX {
+            query_success: true,
+            error_message: String::new(),
+            flags: summary.into_iter().map(ffi::FlagValueAndInfoSummaryCXX::new).collect(),
+        },
+        Err(errmsg) => ffi::ListFlagValueAndInfoResultCXX {
+            query_success: false,
+            error_message: format!("{:?}", errmsg),
+            flags: Vec::new(),
+        },
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
     use crate::test_utils::{
-        create_test_flag_table, create_test_flag_value_list, create_test_package_table,
-        write_bytes_to_temp_file,
+        create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list,
+        create_test_package_table, write_bytes_to_temp_file,
     };
 
     #[test]
@@ -337,54 +471,154 @@
         let flags =
             list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
         let expected = [
-            (
-                String::from("com.android.aconfig.storage.test_1"),
-                String::from("disabled_rw"),
-                StoredFlagType::ReadWriteBoolean,
-                false,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_1"),
-                String::from("enabled_ro"),
-                StoredFlagType::ReadOnlyBoolean,
-                true,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_1"),
-                String::from("enabled_rw"),
-                StoredFlagType::ReadWriteBoolean,
-                true,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_2"),
-                String::from("disabled_rw"),
-                StoredFlagType::ReadWriteBoolean,
-                false,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_2"),
-                String::from("enabled_fixed_ro"),
-                StoredFlagType::FixedReadOnlyBoolean,
-                true,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_2"),
-                String::from("enabled_ro"),
-                StoredFlagType::ReadOnlyBoolean,
-                true,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_4"),
-                String::from("enabled_fixed_ro"),
-                StoredFlagType::FixedReadOnlyBoolean,
-                true,
-            ),
-            (
-                String::from("com.android.aconfig.storage.test_4"),
-                String::from("enabled_rw"),
-                StoredFlagType::ReadWriteBoolean,
-                true,
-            ),
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("disabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("false"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("enabled_ro"),
+                value_type: StoredFlagType::ReadOnlyBoolean,
+                flag_value: String::from("true"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("enabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("true"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("disabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("false"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("enabled_fixed_ro"),
+                value_type: StoredFlagType::FixedReadOnlyBoolean,
+                flag_value: String::from("true"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("enabled_ro"),
+                value_type: StoredFlagType::ReadOnlyBoolean,
+                flag_value: String::from("true"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_4"),
+                flag_name: String::from("enabled_fixed_ro"),
+                value_type: StoredFlagType::FixedReadOnlyBoolean,
+                flag_value: String::from("true"),
+            },
+            FlagValueSummary {
+                package_name: String::from("com.android.aconfig.storage.test_4"),
+                flag_name: String::from("enabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("true"),
+            },
+        ];
+        assert_eq!(flags, expected);
+    }
+
+    #[test]
+    // this test point locks down the flag list with info api
+    fn test_list_flag_with_info() {
+        let package_table =
+            write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap();
+        let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap();
+        let flag_value_list =
+            write_bytes_to_temp_file(&create_test_flag_value_list().into_bytes()).unwrap();
+        let flag_info_list =
+            write_bytes_to_temp_file(&create_test_flag_info_list().into_bytes()).unwrap();
+
+        let package_table_path = package_table.path().display().to_string();
+        let flag_table_path = flag_table.path().display().to_string();
+        let flag_value_list_path = flag_value_list.path().display().to_string();
+        let flag_info_list_path = flag_info_list.path().display().to_string();
+
+        let flags = list_flags_with_info(
+            &package_table_path,
+            &flag_table_path,
+            &flag_value_list_path,
+            &flag_info_list_path,
+        )
+        .unwrap();
+        let expected = [
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("disabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("false"),
+                is_readwrite: true,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("enabled_ro"),
+                value_type: StoredFlagType::ReadOnlyBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: false,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_1"),
+                flag_name: String::from("enabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: true,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("disabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("false"),
+                is_readwrite: true,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("enabled_fixed_ro"),
+                value_type: StoredFlagType::FixedReadOnlyBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: false,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_2"),
+                flag_name: String::from("enabled_ro"),
+                value_type: StoredFlagType::ReadOnlyBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: false,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_4"),
+                flag_name: String::from("enabled_fixed_ro"),
+                value_type: StoredFlagType::FixedReadOnlyBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: false,
+                has_server_override: false,
+                has_local_override: false,
+            },
+            FlagValueAndInfoSummary {
+                package_name: String::from("com.android.aconfig.storage.test_4"),
+                flag_name: String::from("enabled_rw"),
+                value_type: StoredFlagType::ReadWriteBoolean,
+                flag_value: String::from("true"),
+                is_readwrite: true,
+                has_server_override: false,
+                has_local_override: false,
+            },
         ];
         assert_eq!(flags, expected);
     }
diff --git a/tools/aconfig/aconfig_storage_file/src/main.rs b/tools/aconfig/aconfig_storage_file/src/main.rs
index b686274..8b9e38d 100644
--- a/tools/aconfig/aconfig_storage_file/src/main.rs
+++ b/tools/aconfig/aconfig_storage_file/src/main.rs
@@ -17,8 +17,8 @@
 //! `aconfig-storage` is a debugging tool to parse storage files
 
 use aconfig_storage_file::{
-    list_flags, read_file_to_bytes, AconfigStorageError, FlagInfoList, FlagTable, FlagValueList,
-    PackageTable, StorageFileType,
+    list_flags, list_flags_with_info, read_file_to_bytes, AconfigStorageError, FlagInfoList,
+    FlagTable, FlagValueList, PackageTable, StorageFileType,
 };
 
 use clap::{builder::ArgAction, Arg, Command};
@@ -45,7 +45,10 @@
                         .action(ArgAction::Set),
                 )
                 .arg(Arg::new("flag-map").long("flag-map").required(true).action(ArgAction::Set))
-                .arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set)),
+                .arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set))
+                .arg(
+                    Arg::new("flag-info").long("flag-info").required(false).action(ArgAction::Set),
+                ),
         )
 }
 
@@ -87,9 +90,27 @@
             let package_map = sub_matches.get_one::<String>("package-map").unwrap();
             let flag_map = sub_matches.get_one::<String>("flag-map").unwrap();
             let flag_val = sub_matches.get_one::<String>("flag-val").unwrap();
-            let flags = list_flags(package_map, flag_map, flag_val)?;
-            for (package_name, flag_name, flag_type, flag_value) in flags.iter() {
-                println!("{} {} {:?} {}", package_name, flag_name, flag_type, flag_value);
+            let flag_info = sub_matches.get_one::<String>("flag-info");
+            match flag_info {
+                Some(info_file) => {
+                    let flags = list_flags_with_info(package_map, flag_map, flag_val, info_file)?;
+                    for flag in flags.iter() {
+                        println!(
+                            "{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}",
+                            flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,
+                            flag.is_readwrite, flag.has_server_override, flag.has_local_override,
+                        );
+                    }
+                }
+                None => {
+                    let flags = list_flags(package_map, flag_map, flag_val)?;
+                    for flag in flags.iter() {
+                        println!(
+                            "{} {} {} {:?}",
+                            flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,
+                        );
+                    }
+                }
             }
         }
         _ => unreachable!(),
diff --git a/tools/aconfig/aconfig_storage_file/tests/Android.bp b/tools/aconfig/aconfig_storage_file/tests/Android.bp
new file mode 100644
index 0000000..26b7800
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/Android.bp
@@ -0,0 +1,23 @@
+
+cc_test {
+    name: "aconfig_storage_file.test.cpp",
+    team: "trendy_team_android_core_experiments",
+    srcs: [
+        "storage_file_test.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+        "libaconfig_storage_file_cc",
+        "libbase",
+    ],
+    data: [
+        "package.map",
+        "flag.map",
+        "flag.val",
+        "flag.info",
+    ],
+    test_suites: [
+        "device-tests",
+        "general-tests",
+    ],
+}
diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.info b/tools/aconfig/aconfig_storage_file/tests/flag.info
new file mode 100644
index 0000000..6223edf
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/flag.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.map b/tools/aconfig/aconfig_storage_file/tests/flag.map
new file mode 100644
index 0000000..e868f53
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/flag.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/flag.val b/tools/aconfig/aconfig_storage_file/tests/flag.val
new file mode 100644
index 0000000..ed203d4
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/flag.val
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/package.map b/tools/aconfig/aconfig_storage_file/tests/package.map
new file mode 100644
index 0000000..6c46a03
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/package.map
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp b/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp
new file mode 100644
index 0000000..eccbca5
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/tests/storage_file_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 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 <string>
+#include <vector>
+#include <android-base/file.h>
+#include <android-base/result.h>
+#include <gtest/gtest.h>
+#include "aconfig_storage/aconfig_storage_file.hpp"
+
+using namespace android::base;
+using namespace aconfig_storage;
+
+
+void verify_flag(const FlagValueAndInfoSummary& flag,
+                 const std::string& package_name,
+                 const std::string& flag_name,
+                 const std::string& flag_val,
+                 const std::string& value_type,
+                 bool is_readwrite,
+                 bool has_server_override,
+                 bool has_local_override) {
+  ASSERT_EQ(flag.package_name, package_name);
+  ASSERT_EQ(flag.flag_name, flag_name);
+  ASSERT_EQ(flag.flag_value, flag_val);
+  ASSERT_EQ(flag.value_type, value_type);
+  ASSERT_EQ(flag.is_readwrite, is_readwrite);
+  ASSERT_EQ(flag.has_server_override, has_server_override);
+  ASSERT_EQ(flag.has_local_override, has_local_override);
+}
+
+TEST(AconfigStorageFileTest, test_list_flag_with_info) {
+  auto const test_dir = GetExecutableDirectory();
+  auto const package_map = test_dir + "/package.map";
+  auto const flag_map = test_dir + "/flag.map";
+  auto const flag_val = test_dir + "/flag.val";
+  auto const flag_info = test_dir + "/flag.info";
+  auto flag_list_result = aconfig_storage::list_flags_with_info(
+      package_map, flag_map, flag_val, flag_info);
+  ASSERT_TRUE(flag_list_result.ok());
+
+  auto const& flag_list = *flag_list_result;
+  ASSERT_EQ(flag_list.size(), 8);
+  verify_flag(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw",
+              "false", "ReadWriteBoolean", true, false, false);
+  verify_flag(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro",
+              "true", "ReadOnlyBoolean", false, false, false);
+  verify_flag(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw",
+              "true", "ReadWriteBoolean", true, false, false);
+  verify_flag(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw",
+              "false", "ReadWriteBoolean", true, false, false);
+  verify_flag(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro",
+              "true", "FixedReadOnlyBoolean", false, false, false);
+  verify_flag(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro",
+              "true", "ReadOnlyBoolean", false, false, false);
+  verify_flag(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro",
+              "true", "FixedReadOnlyBoolean", false, false, false);
+  verify_flag(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw",
+              "true", "ReadWriteBoolean", true, false, false);
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
index e65dcfb..e419206 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -504,7 +504,7 @@
         let pb_file_path = pb_file.path().display().to_string();
         let flag_info_file =
             unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
-        let is_rw: Vec<bool> = vec![true, false, true, false, false, false, false, false];
+        let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true];
         for (offset, expected_value) in is_rw.into_iter().enumerate() {
             let attribute =
                 get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.info b/tools/aconfig/aconfig_storage_read_api/tests/flag.info
index 820d839..6223edf 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/flag.info
+++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
index b499c1c..cfd128d 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
@@ -232,7 +232,7 @@
   ASSERT_TRUE(mapped_file.ok());
 
   auto expected_value = std::vector<bool>{
-    true, false, true, false, false, false, false, false};
+    true, false, true, true, false, false, false, true};
   for (int index = 0; index < 8; ++index) {
     auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, index);
     ASSERT_TRUE(attribute.ok());
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
index ce9c018..ecba573 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
@@ -188,7 +188,7 @@
         // The safety here is ensured as the test process will not write to temp storage file
         let flag_info_file =
             unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
-        let is_rw: Vec<bool> = vec![true, false, true, false, false, false, false, false];
+        let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true];
         for (offset, expected_value) in is_rw.into_iter().enumerate() {
             let attribute =
                 get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();
diff --git a/tools/aconfig/aflags/src/aconfig_storage_source.rs b/tools/aconfig/aflags/src/aconfig_storage_source.rs
index a3ca221..c21c542 100644
--- a/tools/aconfig/aflags/src/aconfig_storage_source.rs
+++ b/tools/aconfig/aflags/src/aconfig_storage_source.rs
@@ -27,13 +27,13 @@
             let container =
                 file_info.container.ok_or(anyhow!("storage file is missing container"))?;
 
-            for (package, name, _flag_type, val) in
+            for listed_flag in
                 aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)?
             {
                 result.push(Flag {
-                    name: name.to_string(),
-                    package: package.to_string(),
-                    value: FlagValue::try_from(val.to_string().as_str())?,
+                    name: listed_flag.flag_name,
+                    package: listed_flag.package_name,
+                    value: FlagValue::try_from(listed_flag.flag_value.as_str())?,
                     container: container.to_string(),
 
                     // TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI.