Merge "aconfig: add aconfig_storage_metadata proto" into main am: dfb817c223

Original change: https://android-review.googlesource.com/c/platform/build/+/2928251

Change-Id: Id6173a23a30990e740c4a99bf12965d630dcdee4
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Dennis Shen
2024-01-26 21:23:26 +00:00
committed by Automerger Merge Worker
7 changed files with 278 additions and 0 deletions

View File

@@ -9,6 +9,9 @@ rust_defaults {
srcs: ["src/lib.rs"],
rustlibs: [
"libanyhow",
"libaconfig_storage_protos",
"libonce_cell",
"libprotobuf",
],
}
@@ -24,3 +27,11 @@ rust_test_host {
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,
}

View File

@@ -9,3 +9,7 @@ cargo = []
[dependencies]
anyhow = "1.0.69"
protobuf = "3.2.0"
[build-dependencies]
protobuf-codegen = "3.2.0"

View File

@@ -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();
}

View File

@@ -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;
};

View File

@@ -21,6 +21,10 @@ pub mod flag_table;
pub mod flag_value;
pub mod package_table;
mod protos;
#[cfg(test)]
mod test_utils;
use anyhow::{anyhow, Result};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

View File

@@ -0,0 +1,182 @@
/*
* 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;
pub mod storage_files {
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(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::get_binary_storage_proto_bytes;
#[test]
fn test_parse_storage_files() {
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 = get_binary_storage_proto_bytes(text_proto).unwrap();
let storage_files = storage_files::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_files() {
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 = get_binary_storage_proto_bytes(text_proto).unwrap();
let err = storage_files::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 = get_binary_storage_proto_bytes(text_proto).unwrap();
let err = storage_files::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 = get_binary_storage_proto_bytes(text_proto).unwrap();
let err = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
assert_eq!(
format!("{:?}", err),
"invalid storage file record: missing flag val file for container system"
);
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 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.
*/
use anyhow::Result;
use protobuf::Message;
use crate::protos::ProtoStorageFiles;
pub fn get_binary_storage_proto_bytes(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)
}