Merge "Build the general tests host shared libs." into main
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 90ac75f..9f43a3e 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -199,5 +199,12 @@
 
 # Add crashrecovery build flag to soong
 $(call soong_config_set,ANDROID,release_crashrecovery_module,$(RELEASE_CRASHRECOVERY_MODULE))
+ifeq (true,$(RELEASE_CRASHRECOVERY_FILE_MOVE))
+  $(call soong_config_set,ANDROID,crashrecovery_files_in_module,true)
+  $(call soong_config_set,ANDROID,crashrecovery_files_in_platform,false)
+else
+  $(call soong_config_set,ANDROID,crashrecovery_files_in_module,false)
+  $(call soong_config_set,ANDROID,crashrecovery_files_in_platform,true)
+endif
 # Weirdly required because platform_bootclasspath is using AUTO namespace
 $(call soong_config_set,AUTO,release_crashrecovery_module,$(RELEASE_CRASHRECOVERY_MODULE))
diff --git a/core/version_util.mk b/core/version_util.mk
index 0ed4499..6cda0fc 100644
--- a/core/version_util.mk
+++ b/core/version_util.mk
@@ -31,6 +31,7 @@
 #     PLATFORM_VNDK_VERSION
 #     PLATFORM_SYSTEMSDK_VERSIONS
 #     PLATFORM_VERSION_LAST_STABLE
+#     PLATFORM_VERSION_KNOWN_CODENAMES
 #
 
 # Look for an optional file containing overrides of the defaults,
@@ -95,17 +96,10 @@
 PLATFORM_VERSION_LAST_STABLE := $(RELEASE_PLATFORM_VERSION_LAST_STABLE)
 .KATI_READONLY := PLATFORM_VERSION_LAST_STABLE
 
-
-# This are all known codenames. Should this move into the release config?
-PLATFORM_VERSION_KNOWN_CODENAMES := \
-Base Base11 Cupcake Donut Eclair Eclair01 EclairMr1 Froyo Gingerbread GingerbreadMr1 \
-Honeycomb HoneycombMr1 HoneycombMr2 IceCreamSandwich IceCreamSandwichMr1 \
-JellyBean JellyBeanMr1 JellyBeanMr2 Kitkat KitkatWatch Lollipop LollipopMr1 M N NMr1 O OMr1 P \
-Q R S Sv2 Tiramisu UpsideDownCake VanillaIceCream
-
-# Convert from space separated list to comma separated
-PLATFORM_VERSION_KNOWN_CODENAMES := \
-  $(call normalize-comma-list,$(PLATFORM_VERSION_KNOWN_CODENAMES))
+ifdef PLATFORM_VERSION_KNOWN_CODENAMES
+  $(error Do not set PLATFORM_VERSION_KNOWN_CODENAMES directly. Use RELEASE_PLATFORM_VERSION_KNOWN_CODENAMES. value: $(PLATFORM_VERSION_KNOWN_CODENAMES))
+endif
+PLATFORM_VERSION_KNOWN_CODENAMES := $(RELEASE_PLATFORM_VERSION_KNOWN_CODENAMES)
 .KATI_READONLY := PLATFORM_VERSION_KNOWN_CODENAMES
 
 ifndef PLATFORM_VERSION
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 277223e..7d0222d 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -18,6 +18,7 @@
 PRODUCT_PACKAGES += \
     abx \
     adbd_system_api \
+    aflags \
     am \
     android.hidl.base-V1.0-java \
     android.hidl.manager-V1.0-java \
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index 3e3918c..dca9baa 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -74,6 +74,7 @@
     com.android.media:updatable-media \
     com.android.mediaprovider:framework-mediaprovider \
     com.android.mediaprovider:framework-pdf \
+    com.android.mediaprovider:framework-pdf-v \
     com.android.ondevicepersonalization:framework-ondevicepersonalization \
     com.android.os.statsd:framework-statsd \
     com.android.permission:framework-permission \
@@ -108,6 +109,7 @@
 # Keep the list sorted by module names and then library names.
 PRODUCT_APEX_BOOT_JARS_FOR_SOURCE_BUILD_ONLY := \
     com.android.mediaprovider:framework-pdf \
+    com.android.mediaprovider:framework-pdf-v \
 
 # List of system_server classpath jars delivered via apex.
 # Keep the list sorted by module names and then library names.
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
index 970fdcf..95f1215 100644
--- a/tools/aconfig/Cargo.toml
+++ b/tools/aconfig/Cargo.toml
@@ -4,6 +4,7 @@
     "aconfig",
     "aconfig_protos",
     "aconfig_storage_file",
+    "aflags",
     "printflags"
 ]
 
diff --git a/tools/aconfig/TEST_MAPPING b/tools/aconfig/TEST_MAPPING
index 1a3f79a..e42b5d3 100644
--- a/tools/aconfig/TEST_MAPPING
+++ b/tools/aconfig/TEST_MAPPING
@@ -74,6 +74,11 @@
     {
       // aconfig_storage read api cpp integration tests
       "name": "aconfig_storage.test.cpp"
+    },
+    {
+      // aflags CLI unit tests
+      // TODO(b/326062088): add to presubmit once proven in postsubmit.
+      "name": "aflags.test"
     }
   ]
 }
diff --git a/tools/aconfig/aflags/Android.bp b/tools/aconfig/aflags/Android.bp
new file mode 100644
index 0000000..c65da97
--- /dev/null
+++ b/tools/aconfig/aflags/Android.bp
@@ -0,0 +1,29 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+    name: "aflags.defaults",
+    edition: "2021",
+    clippy_lints: "android",
+    lints: "android",
+    srcs: ["src/main.rs"],
+    rustlibs: [
+        "libaconfig_protos",
+        "libanyhow",
+        "libclap",
+        "libprotobuf",
+        "libregex",
+    ],
+}
+
+rust_binary {
+    name: "aflags",
+    defaults: ["aflags.defaults"],
+}
+
+rust_test_host {
+    name: "aflags.test",
+    defaults: ["aflags.defaults"],
+    test_suites: ["general-tests"],
+}
diff --git a/tools/aconfig/aflags/Cargo.toml b/tools/aconfig/aflags/Cargo.toml
new file mode 100644
index 0000000..3350a6cd
--- /dev/null
+++ b/tools/aconfig/aflags/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "aflags"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.69"
+paste = "1.0.11"
+clap = { version = "4", features = ["derive"] }
+protobuf = "3.2.0"
+regex = "1.10.3"
+aconfig_protos = { path = "../aconfig_protos" }
diff --git a/tools/aconfig/aflags/src/device_config_source.rs b/tools/aconfig/aflags/src/device_config_source.rs
new file mode 100644
index 0000000..12a62cf
--- /dev/null
+++ b/tools/aconfig/aflags/src/device_config_source.rs
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+use crate::{Flag, FlagPermission, FlagSource, ValuePickedFrom};
+use aconfig_protos::ProtoFlagPermission as ProtoPermission;
+use aconfig_protos::ProtoFlagState as ProtoState;
+use aconfig_protos::ProtoParsedFlag;
+use aconfig_protos::ProtoParsedFlags;
+use anyhow::{anyhow, bail, Result};
+use regex::Regex;
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::process::Command;
+use std::{fs, str};
+
+pub struct DeviceConfigSource {}
+
+fn convert_parsed_flag(flag: &ProtoParsedFlag) -> Flag {
+    let namespace = flag.namespace().to_string();
+    let package = flag.package().to_string();
+    let name = flag.name().to_string();
+
+    let container = if flag.container().is_empty() {
+        "system".to_string()
+    } else {
+        flag.container().to_string()
+    };
+
+    let value = match flag.state() {
+        ProtoState::ENABLED => "true",
+        ProtoState::DISABLED => "false",
+    }
+    .to_string();
+
+    let permission = match flag.permission() {
+        ProtoPermission::READ_ONLY => FlagPermission::ReadOnly,
+        ProtoPermission::READ_WRITE => FlagPermission::ReadWrite,
+    };
+
+    Flag {
+        namespace,
+        package,
+        name,
+        container,
+        value,
+        permission,
+        value_picked_from: ValuePickedFrom::Default,
+    }
+}
+
+fn read_pb_files() -> Result<Vec<Flag>> {
+    let mut flags: BTreeMap<String, Flag> = BTreeMap::new();
+    for partition in ["system", "system_ext", "product", "vendor"] {
+        let path = format!("/{}/etc/aconfig_flags.pb", partition);
+        let Ok(bytes) = fs::read(&path) else {
+            eprintln!("warning: failed to read {}", path);
+            continue;
+        };
+        let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)?;
+        for flag in parsed_flags.parsed_flag {
+            let key = format!("{}.{}", flag.package(), flag.name());
+            let container = if flag.container().is_empty() {
+                "system".to_string()
+            } else {
+                flag.container().to_string()
+            };
+
+            if container.eq(partition) {
+                flags.insert(key, convert_parsed_flag(&flag));
+            }
+        }
+    }
+    Ok(flags.values().cloned().collect())
+}
+
+fn parse_device_config(raw: &str) -> Result<HashMap<String, String>> {
+    let mut flags = HashMap::new();
+    let regex = Regex::new(r"(?m)^([[[:alnum:]]_]+/[[[:alnum:]]_\.]+)=(true|false)$")?;
+    for capture in regex.captures_iter(raw) {
+        let key =
+            capture.get(1).ok_or(anyhow!("invalid device_config output"))?.as_str().to_string();
+        let value = capture.get(2).ok_or(anyhow!("invalid device_config output"))?.as_str();
+        flags.insert(key, value.to_string());
+    }
+    Ok(flags)
+}
+
+fn read_device_config_output(command: &str) -> Result<String> {
+    let output = Command::new("/system/bin/device_config").arg(command).output()?;
+    if !output.status.success() {
+        let reason = match output.status.code() {
+            Some(code) => format!("exit code {}", code),
+            None => "terminated by signal".to_string(),
+        };
+        bail!("failed to execute device_config: {}", reason);
+    }
+    Ok(str::from_utf8(&output.stdout)?.to_string())
+}
+
+fn read_device_config_flags() -> Result<HashMap<String, String>> {
+    let list_output = read_device_config_output("list")?;
+    parse_device_config(&list_output)
+}
+
+fn reconcile(pb_flags: &[Flag], dc_flags: HashMap<String, String>) -> Vec<Flag> {
+    pb_flags
+        .iter()
+        .map(|f| {
+            dc_flags
+                .get(&format!("{}/{}.{}", f.namespace, f.package, f.name))
+                .map(|value| {
+                    if value.eq(&f.value) {
+                        Flag { value_picked_from: ValuePickedFrom::Default, ..f.clone() }
+                    } else {
+                        Flag {
+                            value_picked_from: ValuePickedFrom::Server,
+                            value: value.to_string(),
+                            ..f.clone()
+                        }
+                    }
+                })
+                .unwrap_or(f.clone())
+        })
+        .collect()
+}
+
+impl FlagSource for DeviceConfigSource {
+    fn list_flags() -> Result<Vec<Flag>> {
+        let pb_flags = read_pb_files()?;
+        let dc_flags = read_device_config_flags()?;
+
+        let flags = reconcile(&pb_flags, dc_flags);
+        Ok(flags)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_parse_device_config() {
+        let input = r#"
+namespace_one/com.foo.bar.flag_one=true
+namespace_one/com.foo.bar.flag_two=false
+random_noise;
+namespace_two/android.flag_one=true
+namespace_two/android.flag_two=nonsense
+"#;
+        let expected = HashMap::from([
+            ("namespace_one/com.foo.bar.flag_one".to_string(), "true".to_string()),
+            ("namespace_one/com.foo.bar.flag_two".to_string(), "false".to_string()),
+            ("namespace_two/android.flag_one".to_string(), "true".to_string()),
+        ]);
+        let actual = parse_device_config(input).unwrap();
+        assert_eq!(expected, actual);
+    }
+}
diff --git a/tools/aconfig/aflags/src/main.rs b/tools/aconfig/aflags/src/main.rs
new file mode 100644
index 0000000..1e2a7a0
--- /dev/null
+++ b/tools/aconfig/aflags/src/main.rs
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+//! `aflags` is a device binary to read and write aconfig flags.
+
+use anyhow::Result;
+use clap::Parser;
+
+mod device_config_source;
+use device_config_source::DeviceConfigSource;
+
+#[derive(Clone)]
+enum FlagPermission {
+    ReadOnly,
+    ReadWrite,
+}
+
+impl ToString for FlagPermission {
+    fn to_string(&self) -> String {
+        match &self {
+            Self::ReadOnly => "read-only".into(),
+            Self::ReadWrite => "read-write".into(),
+        }
+    }
+}
+
+#[derive(Clone)]
+enum ValuePickedFrom {
+    Default,
+    Server,
+}
+
+impl ToString for ValuePickedFrom {
+    fn to_string(&self) -> String {
+        match &self {
+            Self::Default => "default".into(),
+            Self::Server => "server".into(),
+        }
+    }
+}
+
+#[derive(Clone)]
+struct Flag {
+    namespace: String,
+    name: String,
+    package: String,
+    container: String,
+    value: String,
+    permission: FlagPermission,
+    value_picked_from: ValuePickedFrom,
+}
+
+trait FlagSource {
+    fn list_flags() -> Result<Vec<Flag>>;
+}
+
+const ABOUT_TEXT: &str = "Tool for reading and writing flags.
+
+Rows in the table from the `list` command follow this format:
+
+  package flag_name value provenance permission container
+
+  * `package`: package set for this flag in its .aconfig definition.
+  * `flag_name`: flag name, also set in definition.
+  * `value`: the value read from the flag.
+  * `provenance`: one of:
+    + `default`: the flag value comes from its build-time default.
+    + `server`: the flag value comes from a server override.
+  * `permission`: read-write or read-only.
+  * `container`: the container for the flag, configured in its definition.
+";
+
+#[derive(Parser, Debug)]
+#[clap(long_about=ABOUT_TEXT)]
+struct Cli {
+    #[clap(subcommand)]
+    command: Command,
+}
+
+#[derive(Parser, Debug)]
+enum Command {
+    /// List all aconfig flags on this device.
+    List,
+}
+
+struct PaddingInfo {
+    longest_package_col: usize,
+    longest_name_col: usize,
+    longest_val_col: usize,
+    longest_value_picked_from_col: usize,
+    longest_permission_col: usize,
+}
+
+fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String {
+    let pkg = &flag.package;
+    let p0 = info.longest_package_col + 1;
+
+    let name = &flag.name;
+    let p1 = info.longest_name_col + 1;
+
+    let val = flag.value.to_string();
+    let p2 = info.longest_val_col + 1;
+
+    let value_picked_from = flag.value_picked_from.to_string();
+    let p3 = info.longest_value_picked_from_col + 1;
+
+    let perm = flag.permission.to_string();
+    let p4 = info.longest_permission_col + 1;
+
+    let container = &flag.container;
+
+    format!("{pkg:p0$}{name:p1$}{val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n")
+}
+
+fn list() -> Result<String> {
+    let flags = DeviceConfigSource::list_flags()?;
+    let padding_info = PaddingInfo {
+        longest_package_col: flags.iter().map(|f| f.package.len()).max().unwrap_or(0),
+        longest_name_col: flags.iter().map(|f| f.name.len()).max().unwrap_or(0),
+        longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0),
+        longest_value_picked_from_col: flags
+            .iter()
+            .map(|f| f.value_picked_from.to_string().len())
+            .max()
+            .unwrap_or(0),
+        longest_permission_col: flags
+            .iter()
+            .map(|f| f.permission.to_string().len())
+            .max()
+            .unwrap_or(0),
+    };
+
+    let mut result = String::from("");
+    for flag in flags {
+        let row = format_flag_row(&flag, &padding_info);
+        result.push_str(&row);
+    }
+    Ok(result)
+}
+
+fn main() {
+    let cli = Cli::parse();
+    let output = match cli.command {
+        Command::List => list(),
+    };
+    match output {
+        Ok(text) => println!("{text}"),
+        Err(msg) => println!("Error: {}", msg),
+    }
+}
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index e521e1f..dbbbca2 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -261,7 +261,7 @@
       Specify the VABC cow version to be used
 
   --compression_factor
-      Specify the maximum block size to be compressed at once during OTA. supported options: 4k, 8k, 16k, 32k, 64k, 128k
+      Specify the maximum block size to be compressed at once during OTA. supported options: 4k, 8k, 16k, 32k, 64k, 128k, 256k
 """
 
 from __future__ import print_function
@@ -603,7 +603,7 @@
 
   This function modifies ab_partitions list with the desired partitions before
   calling the brillo_update_payload script. It also cleans up the reference to
-  the excluded partitions in the info file, e.g misc_info.txt.
+  the excluded partitions in the info file, e.g. misc_info.txt.
 
   Args:
     input_file: The input target-files.zip filename.
@@ -1276,11 +1276,11 @@
         raise ValueError("Cannot parse value %r for option %r - only "
                          "integers are allowed." % (a, o))
     elif o in ("--compression_factor"):
-        values = ["4k", "8k", "16k", "32k", "64k", "128k"]
+        values = ["4k", "8k", "16k", "32k", "64k", "128k", "256k"]
         if a[:-1].isdigit() and a in values and a.endswith("k"):
             OPTIONS.compression_factor = str(int(a[:-1]) * 1024)
         else:
-            raise ValueError("Please specify value from following options: 4k, 8k, 16k, 32k, 64k, 128k")
+            raise ValueError("Please specify value from following options: 4k, 8k, 16k, 32k, 64k, 128k", "256k")
 
     elif o == "--vabc_cow_version":
       if a.isdigit():