aconfig: Add script to generate finalized flag proto.

Takes /prebuilts/sdk/<level>/finalized-flags.txt and creates
aconfig_internal FinalizedFlags proto. For use in automatically adding
the sdk check in exported flag codegen.

Test
1. m convert_finalized_flags_to_proto
2. /path/to/aosp/out/host/linux-x86/bin/convert_finalized_flags_to_proto /path/to/aosp/prebuits/sdk > tmp_flags.pb
3. inspect proto for correctness

Next steps:
-Add genrule which uses this binary
-Read proto output in codegen

Test: manual, see above
Change-Id: I730a1c20347fe1a11fe7213fa25fffbe4ea3fc23
diff --git a/tools/aconfig/aconfig/data/Android.bp b/tools/aconfig/aconfig/data/Android.bp
new file mode 100644
index 0000000..1b5eef0
--- /dev/null
+++ b/tools/aconfig/aconfig/data/Android.bp
@@ -0,0 +1,14 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_binary_host {
+    name: "convert_finalized_flags_to_proto",
+    srcs: ["convert_finalized_flags_to_proto.py"],
+    libs: ["aconfig_internal_proto_python"],
+    version: {
+        py3: {
+            embedded_launcher: true,
+        },
+    },
+}
diff --git a/tools/aconfig/aconfig/data/convert_finalized_flags_to_proto.py b/tools/aconfig/aconfig/data/convert_finalized_flags_to_proto.py
new file mode 100644
index 0000000..15ff03c
--- /dev/null
+++ b/tools/aconfig/aconfig/data/convert_finalized_flags_to_proto.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+import collections
+import sys
+import os
+
+from io import TextIOWrapper
+from protos import aconfig_internal_pb2
+from typing import Dict, List, Set
+
+def extract_finalized_flags(flag_file: TextIOWrapper):
+  finalized_flags_for_sdk = list()
+
+  for line in f:
+    flag_name = line.strip()
+    if flag_name:
+      finalized_flags_for_sdk.append(flag_name)
+
+  return finalized_flags_for_sdk
+
+def remove_duplicate_flags(all_flags_with_duplicates: Dict[int, List]):
+  result_flags = collections.defaultdict(set)
+
+  for api_level in sorted(all_flags_with_duplicates.keys(), key=int):
+    for flag in all_flags_with_duplicates[api_level]:
+      if not any(flag in value_set for value_set in result_flags.values()):
+        result_flags[api_level].add(flag)
+
+  return result_flags
+
+def build_proto(all_flags: Set):
+  finalized_flags = aconfig_internal_pb2.finalized_flags()
+  for api_level, qualified_name_list in all_flags.items():
+    for qualified_name in qualified_name_list:
+      package_name, flag_name = qualified_name.rsplit('.', 1)
+      finalized_flag = aconfig_internal_pb2.finalized_flag()
+      finalized_flag.name = flag_name
+      finalized_flag.package = package_name
+      finalized_flag.min_sdk = api_level
+      finalized_flags.finalized_flag.append(finalized_flag)
+  return finalized_flags
+
+if __name__ == '__main__':
+  if len(sys.argv) == 1:
+    sys.exit('No prebuilts/sdk directory provided.')
+  all_api_info_dir = sys.argv[1]
+
+  all_flags_with_duplicates = {}
+  for sdk_dir in os.listdir(all_api_info_dir):
+    api_level = sdk_dir.rsplit('/', 1)[0].rstrip('0').rstrip('.')
+
+    # No support for minor versions yet. This also removes non-numeric dirs.
+    # Update once floats are acceptable.
+    if not api_level.isdigit():
+      continue
+
+    flag_file_path = os.path.join(all_api_info_dir, sdk_dir, 'finalized-flags.txt')
+    try:
+      with open(flag_file_path, 'r') as f:
+        finalized_flags_for_sdk = extract_finalized_flags(f)
+        all_flags_with_duplicates[int(api_level)] = finalized_flags_for_sdk
+    except FileNotFoundError:
+      # Either this version is not finalized yet or looking at a
+      # /prebuilts/sdk/version before finalized-flags.txt was introduced.
+      continue
+
+  all_flags = remove_duplicate_flags(all_flags_with_duplicates)
+  finalized_flags = build_proto(all_flags)
+  sys.stdout.buffer.write(finalized_flags.SerializeToString())
diff --git a/tools/aconfig/aconfig_protos/Android.bp b/tools/aconfig/aconfig_protos/Android.bp
index 62a2b64..080688e 100644
--- a/tools/aconfig/aconfig_protos/Android.bp
+++ b/tools/aconfig/aconfig_protos/Android.bp
@@ -98,3 +98,13 @@
     test_suites: ["general-tests"],
     defaults: ["aconfig_protos.defaults"],
 }
+
+// Internal protos
+
+python_library_host {
+    name: "aconfig_internal_proto_python",
+    srcs: ["protos/aconfig_internal.proto"],
+    proto: {
+        canonical_path_from_root: false,
+    },
+}