Merge "fix RE for metalava actions" into main
diff --git a/core/config.mk b/core/config.mk
index a26ad67..90cc33c 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -813,7 +813,11 @@
 requirements :=
 
 # Set default value of KEEP_VNDK.
-KEEP_VNDK ?= true
+ifeq ($(RELEASE_DEPRECATE_VNDK),true)
+  KEEP_VNDK ?= false
+else
+  KEEP_VNDK ?= true
+endif
 
 # BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED can be true only if early-mount of
 # partitions is supported. But the early-mount must be supported for full
diff --git a/tools/aconfig/Android.bp b/tools/aconfig/Android.bp
index e2fadb0..1ad9053 100644
--- a/tools/aconfig/Android.bp
+++ b/tools/aconfig/Android.bp
@@ -58,7 +58,6 @@
         "libaconfig_protos",
         "libanyhow",
         "libclap",
-        "libitertools",
         "libprotobuf",
         "libserde",
         "libserde_json",
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
index 2edf4b8..941b30d 100644
--- a/tools/aconfig/Cargo.toml
+++ b/tools/aconfig/Cargo.toml
@@ -11,7 +11,6 @@
 [dependencies]
 anyhow = "1.0.69"
 clap = { version = "4.1.8", features = ["derive"] }
-itertools = "0.10.5"
 paste = "1.0.11"
 protobuf = "3.2.0"
 serde = { version = "1.0.152", features = ["derive"] }
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 954eccc..05ee0d7 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -15,7 +15,6 @@
  */
 
 use anyhow::Result;
-use itertools::Itertools;
 use serde::Serialize;
 use std::collections::BTreeSet;
 use std::path::PathBuf;
@@ -35,18 +34,12 @@
 {
     let flag_elements: Vec<FlagElement> =
         parsed_flags_iter.map(|pf| create_flag_element(package, pf)).collect();
-    let namespace_set: BTreeSet<String> = flag_elements
-        .iter()
-        .unique_by(|f| &f.device_config_namespace)
-        .map(|f| f.device_config_namespace.clone())
-        .collect();
     let properties_set: BTreeSet<String> =
         flag_elements.iter().map(|fe| format_property_name(&fe.device_config_namespace)).collect();
     let is_read_write = flag_elements.iter().any(|elem| elem.is_read_write);
     let is_test_mode = codegen_mode == CodegenMode::Test;
     let context = Context {
         flag_elements,
-        namespace_set,
         is_test_mode,
         is_read_write,
         properties_set,
@@ -82,7 +75,6 @@
 #[derive(Serialize)]
 struct Context {
     pub flag_elements: Vec<FlagElement>,
-    pub namespace_set: BTreeSet<String>,
     pub is_test_mode: bool,
     pub is_read_write: bool,
     pub properties_set: BTreeSet<String>,
@@ -297,31 +289,7 @@
         import android.provider.DeviceConfig.Properties;
         /** @hide */
         public final class FeatureFlagsImpl implements FeatureFlags {
-            private static boolean aconfig_test_is_cached = false;
-            private static boolean disabledRw = false;
-            private static boolean enabledRw = true;
-
-
-            private void load_overrides_aconfig_test() {
-                try {
-                    Properties properties = DeviceConfig.getProperties("aconfig_test");
-                    disabledRw =
-                        properties.getBoolean("com.android.aconfig.test.disabled_rw", false);
-                    enabledRw =
-                        properties.getBoolean("com.android.aconfig.test.enabled_rw", true);
-                } catch (NullPointerException e) {
-                    throw new RuntimeException(
-                        "Cannot read value from namespace aconfig_test "
-                        + "from DeviceConfig. It could be that the code using flag "
-                        + "executed before SettingsProvider initialization. Please use "
-                        + "fixed read-only flag by adding is_fixed_read_only: true in "
-                        + "flag declaration.",
-                        e
-                    );
-                }
-                aconfig_test_is_cached = true;
-            }
-
+            private Properties mPropertiesAconfigTest;
             @Override
             @UnsupportedAppUsage
             public boolean disabledRo() {
@@ -330,10 +298,18 @@
             @Override
             @UnsupportedAppUsage
             public boolean disabledRw() {
-                if (!aconfig_test_is_cached) {
-                    load_overrides_aconfig_test();
+                if (mPropertiesAconfigTest == null) {
+                    mPropertiesAconfigTest =
+                        getProperties(
+                            "aconfig_test",
+                            "com.android.aconfig.test.disabled_rw"
+                        );
                 }
-                return disabledRw;
+                return mPropertiesAconfigTest
+                    .getBoolean(
+                        "com.android.aconfig.test.disabled_rw",
+                        false
+                    );
             }
             @Override
             @UnsupportedAppUsage
@@ -348,10 +324,36 @@
             @Override
             @UnsupportedAppUsage
             public boolean enabledRw() {
-                if (!aconfig_test_is_cached) {
-                    load_overrides_aconfig_test();
+                if (mPropertiesAconfigTest == null) {
+                    mPropertiesAconfigTest =
+                        getProperties(
+                            "aconfig_test",
+                            "com.android.aconfig.test.enabled_rw"
+                        );
                 }
-                return enabledRw;
+                return mPropertiesAconfigTest
+                    .getBoolean(
+                        "com.android.aconfig.test.enabled_rw",
+                        true
+                    );
+            }
+            private Properties getProperties(
+                String namespace,
+                String flagName) {
+                Properties properties = null;
+                try {
+                    properties = DeviceConfig.getProperties(namespace);
+                } catch (NullPointerException e) {
+                    throw new RuntimeException(
+                        "Cannot read value of flag " + flagName + " from DeviceConfig. "
+                        + "It could be that the code using flag executed "
+                        + "before SettingsProvider initialization. "
+                        + "Please use fixed read-only flag by adding "
+                        + "is_fixed_read_only: true in flag declaration.",
+                        e
+                    );
+                }
+                return properties;
             }
         }
         "#;
diff --git a/tools/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/templates/FeatureFlagsImpl.java.template
index 87d567c..ff089df 100644
--- a/tools/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/templates/FeatureFlagsImpl.java.template
@@ -8,41 +8,10 @@
 {{ endif }}
 /** @hide */
 public final class FeatureFlagsImpl implements FeatureFlags \{
-{{- if is_read_write }}
-{{- for namespace in namespace_set }}
-    private static boolean {namespace}_is_cached = false;
-{{- endfor- }}
-
-{{ for flag in flag_elements }}
-{{- if flag.is_read_write }}
-    private static boolean {flag.method_name} = {flag.default_value};
-{{- endif- }}
+{{ if is_read_write- }}
+{{ for properties in properties_set }}
+    private Properties {properties};
 {{ endfor }}
-
-{{ for namespace in namespace_set }}
-    private void load_overrides_{namespace}() \{
-        try \{
-            Properties properties = DeviceConfig.getProperties("{namespace}");
-
-            {{- for flag in flag_elements }}
-            {{- if flag.is_read_write }}
-            {flag.method_name} =
-                properties.getBoolean("{flag.device_config_flag}", {flag.default_value});
-            {{- endif- }}
-            {{ endfor }}
-        } catch (NullPointerException e) \{
-            throw new RuntimeException(
-                "Cannot read value from namespace {namespace} "
-                + "from DeviceConfig. It could be that the code using flag "
-                + "executed before SettingsProvider initialization. Please use "
-                + "fixed read-only flag by adding is_fixed_read_only: true in "
-                + "flag declaration.",
-                e
-            );
-        }
-        {namespace}_is_cached = true;
-    }
-{{ endfor- }}
 {{ endif- }}
 
 {{ for flag in flag_elements }}
@@ -50,15 +19,45 @@
     @UnsupportedAppUsage
     public boolean {flag.method_name}() \{
     {{ -if flag.is_read_write }}
-        if (!{flag.device_config_namespace}_is_cached) \{
-            load_overrides_{flag.device_config_namespace}();
+        if ({flag.properties} == null) \{
+            {flag.properties} =
+                getProperties(
+                    "{flag.device_config_namespace}",
+                    "{flag.device_config_flag}"
+                );
         }
-        return {flag.method_name};
+        return {flag.properties}
+                .getBoolean(
+                    "{flag.device_config_flag}",
+                    {flag.default_value}
+                );
     {{ else }}
         return {flag.default_value};
     {{ endif- }}
     }
 {{ endfor }}
+
+{{ -if is_read_write }}
+    private Properties getProperties(
+            String namespace,
+            String flagName) \{
+        Properties properties = null;
+        try \{
+            properties = DeviceConfig.getProperties(namespace);
+        } catch (NullPointerException e) \{
+            throw new RuntimeException(
+                "Cannot read value of flag " + flagName + " from DeviceConfig. "
+                + "It could be that the code using flag executed "
+                + "before SettingsProvider initialization. "
+                + "Please use fixed read-only flag by adding "
+                + "is_fixed_read_only: true in flag declaration.",
+                e
+            );
+        }
+
+        return properties;
+    }
+{{ endif- }}
 }
 {{ else }}
 {#- Generate only stub if in test mode #}
@@ -71,6 +70,6 @@
         throw new UnsupportedOperationException(
             "Method is not implemented.");
     }
-{{ endfor- }}
+{{ endfor }}
 }
 {{ endif }}
diff --git a/tools/metadata/OWNERS b/tools/metadata/OWNERS
new file mode 100644
index 0000000..03bcdf1
--- /dev/null
+++ b/tools/metadata/OWNERS
@@ -0,0 +1,4 @@
+dariofreni@google.com
+joeo@google.com
+ronish@google.com
+caditya@google.com
diff --git a/tools/metadata/generator.go b/tools/metadata/generator.go
index 8e82f7f..eb87755 100644
--- a/tools/metadata/generator.go
+++ b/tools/metadata/generator.go
@@ -73,7 +73,7 @@
 	return string(data)
 }
 
-func processProtobuf(
+func processTestSpecProtobuf(
 	filePath string, ownershipMetadataMap *sync.Map, keyLocks *keyToLocksMap,
 	errCh chan error, wg *sync.WaitGroup,
 ) {
@@ -130,10 +130,11 @@
 func main() {
 	inputFile := flag.String("inputFile", "", "Input file path")
 	outputFile := flag.String("outputFile", "", "Output file path")
+	rule := flag.String("rule", "", "Metadata rule (Hint: test_spec or code_metadata)")
 	flag.Parse()
 
-	if *inputFile == "" || *outputFile == "" {
-		fmt.Println("Usage: metadata -inputFile <input file path> -outputFile <output file path>")
+	if *inputFile == "" || *outputFile == "" || *rule == "" {
+		fmt.Println("Usage: metadata -rule <rule> -inputFile <input file path> -outputFile <output file path>")
 		os.Exit(1)
 	}
 
@@ -144,26 +145,33 @@
 	errCh := make(chan error, len(filePaths))
 	var wg sync.WaitGroup
 
-	for _, filePath := range filePaths {
-		wg.Add(1)
-		go processProtobuf(filePath, ownershipMetadataMap, keyLocks, errCh, &wg)
+	switch *rule {
+	case "test_spec":
+		for _, filePath := range filePaths {
+			wg.Add(1)
+			go processTestSpecProtobuf(filePath, ownershipMetadataMap, keyLocks, errCh, &wg)
+		}
+
+		wg.Wait()
+		close(errCh)
+
+		for err := range errCh {
+			log.Fatal(err)
+		}
+
+		allKeys := getSortedKeys(ownershipMetadataMap)
+		var allMetadata []*test_spec_proto.TestSpec_OwnershipMetadata
+
+		for _, key := range allKeys {
+			value, _ := ownershipMetadataMap.Load(key)
+			metadataList := value.([]*test_spec_proto.TestSpec_OwnershipMetadata)
+			allMetadata = append(allMetadata, metadataList...)
+		}
+
+		writeOutput(*outputFile, allMetadata)
+		break
+	case "code_metadata":
+	default:
+		log.Fatalf("No specific processing implemented for rule '%s'.\n", *rule)
 	}
-
-	wg.Wait()
-	close(errCh)
-
-	for err := range errCh {
-		log.Fatal(err)
-	}
-
-	allKeys := getSortedKeys(ownershipMetadataMap)
-	var allMetadata []*test_spec_proto.TestSpec_OwnershipMetadata
-
-	for _, key := range allKeys {
-		value, _ := ownershipMetadataMap.Load(key)
-		metadataList := value.([]*test_spec_proto.TestSpec_OwnershipMetadata)
-		allMetadata = append(allMetadata, metadataList...)
-	}
-
-	writeOutput(*outputFile, allMetadata)
 }
diff --git a/tools/metadata/testdata/metadata_test.go b/tools/metadata/testdata/metadata_test.go
index 0cb80c3..03c4f29 100644
--- a/tools/metadata/testdata/metadata_test.go
+++ b/tools/metadata/testdata/metadata_test.go
@@ -10,7 +10,7 @@
 
 func TestMetadata(t *testing.T) {
 	cmd := exec.Command(
-		"metadata", "-inputFile", "./inputFiles.txt", "-outputFile",
+		"metadata", "-rule", "test_spec", "-inputFile", "./inputFiles.txt", "-outputFile",
 		"./generatedOutputFile.txt",
 	)
 	stderr, err := cmd.CombinedOutput()
@@ -40,7 +40,7 @@
 
 func TestMetadataNegativeCase(t *testing.T) {
 	cmd := exec.Command(
-		"metadata", "-inputFile", "./inputFilesNegativeCase.txt", "-outputFile",
+		"metadata", "-rule", "test_spec", "-inputFile", "./inputFilesNegativeCase.txt", "-outputFile",
 		"./generatedOutputFileNegativeCase.txt",
 	)
 	stderr, err := cmd.CombinedOutput()
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 2a7d23b..8ce6083 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -39,18 +39,23 @@
 import threading
 import time
 import zipfile
+
+from typing import Iterable, Callable
 from dataclasses import dataclass
-from genericpath import isdir
 from hashlib import sha1, sha256
 
 import images
-import rangelib
 import sparse_img
 from blockimgdiff import BlockImageDiff
 
 logger = logging.getLogger(__name__)
 
 
+@dataclass
+class OptionHandler:
+  extra_long_opts: Iterable[str]
+  handler: Callable
+
 class Options(object):
 
   def __init__(self):
@@ -2793,12 +2798,19 @@
 def ParseOptions(argv,
                  docstring,
                  extra_opts="", extra_long_opts=(),
-                 extra_option_handler=None):
+                 extra_option_handler: Iterable[OptionHandler] = None):
   """Parse the options in argv and return any arguments that aren't
   flags.  docstring is the calling module's docstring, to be displayed
   for errors and -h.  extra_opts and extra_long_opts are for flags
   defined by the caller, which are processed by passing them to
   extra_option_handler."""
+  extra_long_opts = list(extra_long_opts)
+  if not isinstance(extra_option_handler, Iterable):
+    extra_option_handler = [extra_option_handler]
+
+  for handler in extra_option_handler:
+    if isinstance(handler, OptionHandler):
+      extra_long_opts.extend(handler.extra_long_opts)
 
   try:
     opts, args = getopt.getopt(
@@ -2860,8 +2872,19 @@
     elif o in ("--logfile",):
       OPTIONS.logfile = a
     else:
-      if extra_option_handler is None or not extra_option_handler(o, a):
-        assert False, "unknown option \"%s\"" % (o,)
+      if extra_option_handler is None:
+        raise ValueError("unknown option \"%s\"" % (o,))
+      success = False
+      for handler in extra_option_handler:
+        if isinstance(handler, OptionHandler):
+          if handler.handler(o, a):
+            success = True
+            break
+        elif handler(o, a):
+          success = True
+      if not success:
+        raise ValueError("unknown option \"%s\"" % (o,))
+
 
   if OPTIONS.search_path:
     os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index de0e187..fa4ed09 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -265,7 +265,6 @@
 import os
 import os.path
 import re
-import shlex
 import shutil
 import subprocess
 import sys
@@ -274,6 +273,7 @@
 import care_map_pb2
 import common
 import ota_utils
+import payload_signer
 from ota_utils import (VABC_COMPRESSION_PARAM_SUPPORT, FinalizeMetadata, GetPackageMetadata,
                        PayloadGenerator, SECURITY_PATCH_LEVEL_PROP_NAME, ExtractTargetFiles, CopyTargetFilesDir)
 from common import DoesInputFileContain, IsSparseImage
@@ -308,9 +308,6 @@
 OPTIONS.cache_size = None
 OPTIONS.stash_threshold = 0.8
 OPTIONS.log_diff = None
-OPTIONS.payload_signer = None
-OPTIONS.payload_signer_args = []
-OPTIONS.payload_signer_maximum_signature_size = None
 OPTIONS.extracted_input = None
 OPTIONS.skip_postinstall = False
 OPTIONS.skip_compatibility_check = False
@@ -1125,9 +1122,7 @@
 def main(argv):
 
   def option_handler(o, a):
-    if o in ("-k", "--package_key"):
-      OPTIONS.package_key = a
-    elif o in ("-i", "--incremental_from"):
+    if o in ("-i", "--incremental_from"):
       OPTIONS.incremental_source = a
     elif o == "--full_radio":
       OPTIONS.full_radio = True
@@ -1172,17 +1167,6 @@
                          "a float" % (a, o))
     elif o == "--log_diff":
       OPTIONS.log_diff = a
-    elif o == "--payload_signer":
-      OPTIONS.payload_signer = a
-    elif o == "--payload_signer_args":
-      OPTIONS.payload_signer_args = shlex.split(a)
-    elif o == "--payload_signer_maximum_signature_size":
-      OPTIONS.payload_signer_maximum_signature_size = a
-    elif o == "--payload_signer_key_size":
-      # TODO(Xunchang) remove this option after cleaning up the callers.
-      logger.warning("The option '--payload_signer_key_size' is deprecated."
-                     " Use '--payload_signer_maximum_signature_size' instead.")
-      OPTIONS.payload_signer_maximum_signature_size = a
     elif o == "--extracted_input_target_files":
       OPTIONS.extracted_input = a
     elif o == "--skip_postinstall":
@@ -1258,7 +1242,6 @@
   args = common.ParseOptions(argv, __doc__,
                              extra_opts="b:k:i:d:e:t:2o:",
                              extra_long_opts=[
-                                 "package_key=",
                                  "incremental_from=",
                                  "full_radio",
                                  "full_bootloader",
@@ -1277,10 +1260,6 @@
                                  "verify",
                                  "stash_threshold=",
                                  "log_diff=",
-                                 "payload_signer=",
-                                 "payload_signer_args=",
-                                 "payload_signer_maximum_signature_size=",
-                                 "payload_signer_key_size=",
                                  "extracted_input_target_files=",
                                  "skip_postinstall",
                                  "retrofit_dynamic_partitions",
@@ -1304,7 +1283,7 @@
                                  "vabc_compression_param=",
                                  "security_patch_level=",
                                  "max_threads=",
-                             ], extra_option_handler=option_handler)
+                             ], extra_option_handler=[option_handler, payload_signer.signer_options])
   common.InitLogging()
 
   if len(args) != 2:
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 6ca9d64..0a6ff39 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -37,7 +37,6 @@
 OPTIONS.wipe_user_data = False
 OPTIONS.downgrade = False
 OPTIONS.key_passwords = {}
-OPTIONS.package_key = None
 OPTIONS.incremental_source = None
 OPTIONS.retrofit_dynamic_partitions = False
 OPTIONS.output_metadata_path = None
diff --git a/tools/releasetools/payload_signer.py b/tools/releasetools/payload_signer.py
index bbd2896..a5d09e1 100644
--- a/tools/releasetools/payload_signer.py
+++ b/tools/releasetools/payload_signer.py
@@ -16,10 +16,51 @@
 
 import common
 import logging
-from common import OPTIONS
+import shlex
+from common import OPTIONS, OptionHandler
 
 logger = logging.getLogger(__name__)
 
+OPTIONS.payload_signer = None
+OPTIONS.payload_signer_args = []
+OPTIONS.payload_signer_maximum_signature_size = None
+OPTIONS.package_key = None
+
+
+class SignerOptions(OptionHandler):
+
+  @staticmethod
+  def ParseOptions(o, a):
+    if o in ("-k", "--package_key"):
+      OPTIONS.package_key = a
+    elif o == "--payload_signer":
+      OPTIONS.payload_signer = a
+    elif o == "--payload_signer_args":
+      OPTIONS.payload_signer_args = shlex.split(a)
+    elif o == "--payload_signer_maximum_signature_size":
+      OPTIONS.payload_signer_maximum_signature_size = a
+    elif o == "--payload_signer_key_size":
+      # TODO(xunchang) remove this option after cleaning up the callers.
+      logger.warning("The option '--payload_signer_key_size' is deprecated."
+                      " Use '--payload_signer_maximum_signature_size' instead.")
+      OPTIONS.payload_signer_maximum_signature_size = a
+    else:
+      return False
+    return True
+
+  def __init__(self):
+    super().__init__(
+      ["payload_signer=",
+       "package_key=",
+       "payload_signer_args=",
+       "payload_signer_maximum_signature_size=",
+       "payload_signer_key_size="],
+       SignerOptions.ParseOptions
+    )
+
+
+signer_options = SignerOptions()
+
 
 class PayloadSigner(object):
   """A class that wraps the payload signing works.