aconfig: move aconfig_storage_metadata proto and its apis to
aconfig_storage_file crate
Bug: b/321077378
Test: m aconfig_storage_file.test; m aconfig_storage_read_api.test
Change-Id: Ifeeeff62dc09e172b7e88c45860d2febccc29570
diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp
index 1d74f69..c089d54 100644
--- a/tools/aconfig/aconfig_storage_file/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -11,6 +11,9 @@
"libanyhow",
"libthiserror",
"libtempfile",
+ "libprotobuf",
+ "libclap",
+ "libaconfig_storage_protos",
],
}
@@ -26,3 +29,25 @@
test_suites: ["general-tests"],
defaults: ["aconfig_storage_file.defaults"],
}
+
+rust_protobuf {
+ name: "libaconfig_storage_protos",
+ protos: ["protos/aconfig_storage_metadata.proto"],
+ crate_name: "aconfig_storage_protos",
+ 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,
+}
diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml
index 6a9483e..9b9a615 100644
--- a/tools/aconfig/aconfig_storage_file/Cargo.toml
+++ b/tools/aconfig/aconfig_storage_file/Cargo.toml
@@ -9,11 +9,8 @@
[dependencies]
anyhow = "1.0.69"
-memmap2 = "0.8.0"
protobuf = "3.2.0"
-once_cell = "1.19.0"
tempfile = "3.9.0"
-cxx = "1.0"
thiserror = "1.0.56"
clap = { version = "4.1.8", features = ["derive"] }
diff --git a/tools/aconfig/aconfig_storage_file/build.rs b/tools/aconfig/aconfig_storage_file/build.rs
new file mode 100644
index 0000000..1feeb60
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/build.rs
@@ -0,0 +1,17 @@
+use protobuf_codegen::Codegen;
+
+fn main() {
+ let proto_files = vec!["protos/aconfig_storage_metadata.proto"];
+
+ // tell cargo to only re-run the build script if any of the proto files has changed
+ for path in &proto_files {
+ println!("cargo:rerun-if-changed={}", path);
+ }
+
+ Codegen::new()
+ .pure()
+ .include("protos")
+ .inputs(proto_files)
+ .cargo_out_dir("aconfig_storage_protos")
+ .run_from_script();
+}
diff --git a/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto b/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto
new file mode 100644
index 0000000..c6728bd
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto
@@ -0,0 +1,34 @@
+// 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
+
+// This is the schema definition for aconfig files. Modifications need to be
+// either backwards compatible, or include updates to all aconfig files in the
+// Android tree.
+
+syntax = "proto2";
+
+package android.aconfig_storage_metadata;
+
+message storage_file_info {
+ optional uint32 version = 1;
+ optional string container = 2;
+ optional string package_map = 3;
+ optional string flag_map = 4;
+ optional string flag_val = 5;
+ optional int64 timestamp = 6;
+}
+
+message storage_files {
+ repeated storage_file_info files = 1;
+};
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index 7544838..ec41a4e 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -35,6 +35,7 @@
pub mod flag_table;
pub mod flag_value;
pub mod package_table;
+pub mod protos;
#[cfg(test)]
mod test_utils;
diff --git a/tools/aconfig/aconfig_storage_file/src/protos.rs b/tools/aconfig/aconfig_storage_file/src/protos.rs
new file mode 100644
index 0000000..8b86205
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/protos.rs
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+// When building with the Android tool-chain
+//
+// - an external crate `aconfig_storage_metadata_protos` will be generated
+// - the feature "cargo" will be disabled
+//
+// When building with cargo
+//
+// - a local sub-module will be generated in OUT_DIR and included in this file
+// - the feature "cargo" will be enabled
+//
+// This module hides these differences from the rest of the codebase.
+
+// ---- When building with the Android tool-chain ----
+#[cfg(not(feature = "cargo"))]
+mod auto_generated {
+ pub use aconfig_storage_protos::aconfig_storage_metadata as ProtoStorage;
+ pub use ProtoStorage::Storage_file_info as ProtoStorageFileInfo;
+ pub use ProtoStorage::Storage_files as ProtoStorageFiles;
+}
+
+// ---- When building with cargo ----
+#[cfg(feature = "cargo")]
+mod auto_generated {
+ // include! statements should be avoided (because they import file contents verbatim), but
+ // because this is only used during local development, and only if using cargo instead of the
+ // Android tool-chain, we allow it
+ include!(concat!(env!("OUT_DIR"), "/aconfig_storage_protos/mod.rs"));
+ pub use aconfig_storage_metadata::Storage_file_info as ProtoStorageFileInfo;
+ pub use aconfig_storage_metadata::Storage_files as ProtoStorageFiles;
+}
+
+// ---- Common for both the Android tool-chain and cargo ----
+pub use auto_generated::*;
+
+use anyhow::Result;
+use protobuf::Message;
+use std::io::Write;
+use tempfile::NamedTempFile;
+
+pub mod storage_record_pb {
+ use super::*;
+ use anyhow::ensure;
+
+ pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoStorageFiles> {
+ let message: ProtoStorageFiles = protobuf::Message::parse_from_bytes(bytes)?;
+ verify_fields(&message)?;
+ Ok(message)
+ }
+
+ pub fn verify_fields(storage_files: &ProtoStorageFiles) -> Result<()> {
+ for storage_file_info in storage_files.files.iter() {
+ ensure!(
+ !storage_file_info.package_map().is_empty(),
+ "invalid storage file record: missing package map file for container {}",
+ storage_file_info.container()
+ );
+ ensure!(
+ !storage_file_info.flag_map().is_empty(),
+ "invalid storage file record: missing flag map file for container {}",
+ storage_file_info.container()
+ );
+ ensure!(
+ !storage_file_info.flag_val().is_empty(),
+ "invalid storage file record: missing flag val file for container {}",
+ storage_file_info.container()
+ );
+ }
+ Ok(())
+ }
+
+ pub fn get_binary_proto_from_text_proto(text_proto: &str) -> Result<Vec<u8>> {
+ let storage_files: ProtoStorageFiles = protobuf::text_format::parse_from_str(text_proto)?;
+ let mut binary_proto = Vec::new();
+ storage_files.write_to_vec(&mut binary_proto)?;
+ Ok(binary_proto)
+ }
+
+ pub fn write_proto_to_temp_file(text_proto: &str) -> Result<NamedTempFile> {
+ let bytes = get_binary_proto_from_text_proto(text_proto).unwrap();
+ let mut file = NamedTempFile::new()?;
+ let _ = file.write_all(&bytes);
+ Ok(file)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_parse_storage_record_pb() {
+ let text_proto = r#"
+files {
+ version: 0
+ container: "system"
+ package_map: "/system/etc/package.map"
+ flag_map: "/system/etc/flag.map"
+ flag_val: "/metadata/aconfig/system.val"
+ timestamp: 12345
+}
+files {
+ version: 1
+ container: "product"
+ package_map: "/product/etc/package.map"
+ flag_map: "/product/etc/flag.map"
+ flag_val: "/metadata/aconfig/product.val"
+ timestamp: 54321
+}
+"#;
+ let binary_proto_bytes =
+ storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+ let storage_files = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap();
+ assert_eq!(storage_files.files.len(), 2);
+ let system_file = &storage_files.files[0];
+ assert_eq!(system_file.version(), 0);
+ assert_eq!(system_file.container(), "system");
+ assert_eq!(system_file.package_map(), "/system/etc/package.map");
+ assert_eq!(system_file.flag_map(), "/system/etc/flag.map");
+ assert_eq!(system_file.flag_val(), "/metadata/aconfig/system.val");
+ assert_eq!(system_file.timestamp(), 12345);
+ let product_file = &storage_files.files[1];
+ assert_eq!(product_file.version(), 1);
+ assert_eq!(product_file.container(), "product");
+ assert_eq!(product_file.package_map(), "/product/etc/package.map");
+ assert_eq!(product_file.flag_map(), "/product/etc/flag.map");
+ assert_eq!(product_file.flag_val(), "/metadata/aconfig/product.val");
+ assert_eq!(product_file.timestamp(), 54321);
+ }
+
+ #[test]
+ fn test_parse_invalid_storage_record_pb() {
+ let text_proto = r#"
+files {
+ version: 0
+ container: "system"
+ package_map: ""
+ flag_map: "/system/etc/flag.map"
+ flag_val: "/metadata/aconfig/system.val"
+ timestamp: 12345
+}
+"#;
+ let binary_proto_bytes =
+ storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+ let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "invalid storage file record: missing package map file for container system"
+ );
+
+ let text_proto = r#"
+files {
+ version: 0
+ container: "system"
+ package_map: "/system/etc/package.map"
+ flag_map: ""
+ flag_val: "/metadata/aconfig/system.val"
+ timestamp: 12345
+}
+"#;
+ let binary_proto_bytes =
+ storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+ let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "invalid storage file record: missing flag map file for container system"
+ );
+
+ let text_proto = r#"
+files {
+ version: 0
+ container: "system"
+ package_map: "/system/etc/package.map"
+ flag_map: "/system/etc/flag.map"
+ flag_val: ""
+ timestamp: 12345
+}
+"#;
+ let binary_proto_bytes =
+ storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+ let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+ assert_eq!(
+ format!("{:?}", err),
+ "invalid storage file record: missing flag val file for container system"
+ );
+ }
+}