Camera: Generate HIDL metadata modules

- Add HidlMetadata.mako template for creating HIDL types.hal files
- Add necessary helpers and model properties to easily find sections and entries added in a given
  HIDL version
- Ensure the HIDL version also shows up in the first entry of each section in the camera_metadata
  C headers
- Add HIDL output to metadata-generate (though each new HIDL version needs to have a few lines added)

Bug: 33262893
Test: Verify generated HIDL output manually, build, and HIDL built-in sanity checks.
  Add a few test entries and sections with different HIDL versions to see that generated code is right.
Change-Id: I9ee3b0839bab4b3efed415dab4208bacdbad56b1
diff --git a/camera/docs/HidlMetadata.mako b/camera/docs/HidlMetadata.mako
new file mode 100644
index 0000000..196dab5
--- /dev/null
+++ b/camera/docs/HidlMetadata.mako
@@ -0,0 +1,124 @@
+## -*- coding: utf-8 -*-
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Autogenerated from camera metadata definitions in
+ * /system/media/camera/docs/metadata_definitions.xml
+ * *** DO NOT EDIT BY HAND ***
+ */
+
+<%!
+  def annotated_type(entry):
+    if entry.enum:
+       type = 'enum'
+    else:
+       type = entry.type
+    if entry.container == 'array':
+       type += '[]'
+
+    return type
+%>\
+
+package android.hardware.camera.metadata@${hal_major_version()}.${hal_minor_version()};
+
+% if first_hal_minor_version(hal_major_version()) != hal_minor_version():
+/* Include definitions from all prior minor HAL metadata revisions */
+  % for i in range(first_hal_minor_version(hal_major_version()),hal_minor_version()):
+import android.hardware.camera.metadata@${hal_major_version()}.${i};
+  % endfor
+
+% endif
+/**
+ * Top level hierarchy definitions for camera metadata. *_INFO sections are for
+ * the static metadata that can be retrived without opening the camera device.
+ */
+enum CameraMetadataSection : ${'uint32_t' if first_hal_minor_version(hal_major_version()) == hal_minor_version() else 'android.hardware.camera.metadata@%d.%d::CameraMetadataSection' % (hal_major_version(), hal_minor_version()-1)} {
+% for idx, section in enumerate(find_all_sections_added_in_hal(metadata, hal_major_version(), hal_minor_version())):
+  % if idx == 0 and first_hal_minor_version(hal_major_version()) != hal_minor_version():
+    ${path_name(section) | csym} =
+        android.hardware.camera.metadata@${hal_major_version()}.${hal_minor_version()-1}::CameraMetadataSection::ANDROID_SECTION_COUNT,
+  % else:
+    ${path_name(section) | csym},
+  % endif
+
+% endfor
+    ANDROID_SECTION_COUNT,
+
+    VENDOR_SECTION = 0x8000,
+
+};
+
+/**
+ * Hierarchy positions in enum space. All vendor extension sections must be
+ * defined with tag >= VENDOR_SECTION_START
+ */
+enum CameraMetadataSectionStart : ${'uint32_t' if first_hal_minor_version(hal_major_version()) == hal_minor_version() else 'android.hardware.camera.metadata@%d.%d::CameraMetadataSectionStart' % (hal_major_version(), hal_minor_version()-1)} {
+  % for i in find_all_sections_added_in_hal(metadata, hal_major_version(), hal_minor_version()):
+    ${path_name(i) + '.start' | csym} = CameraMetadataSection:${path_name(i) | csym} << 16,
+
+  % endfor
+    VENDOR_SECTION_START = CameraMetadataSection:VENDOR_SECTION << 16,
+
+};
+
+/**
+ * Main enum for defining camera metadata tags. New entries must always go
+ * before the section _END tag to preserve existing enumeration values.
+ */
+enum CameraMetadataTag : ${'uint32_t' if first_hal_minor_version(hal_major_version()) == hal_minor_version() else 'android.hardware.camera.metadata@%d.%d::CameraMetadataTag' % (hal_major_version(), hal_minor_version()-1)} {
+    % for sec in find_all_sections(metadata):
+<%    gotEntries = False %>\
+      % for idx,entry in enumerate(filter_added_in_hal_version(remove_synthetic(find_unique_entries(sec)), hal_major_version(), hal_minor_version())):
+<%      gotEntries = True %>\
+        % if idx == 0:
+    ${entry.name + " = " | csym,ljust(50)}// ${annotated_type(entry) | ljust(12) } | ${entry.applied_visibility}
+          % if find_first_older_used_hal_version(sec, hal_major_version(), hal_minor_version()) == (0, 0):
+            CameraMetadataSectionStart:${path_name(find_parent_section(entry)) | csym}_START,
+          % else:
+            ${'android.hardware.camera.metadata@%d.%d' % find_first_older_used_hal_version(sec, hal_major_version(), hal_minor_version())}::CameraMetadataTag:${path_name(find_parent_section(entry)) | csym}_END,
+          % endif
+        % else:
+    ${entry.name + "," | csym,ljust(50)}// ${annotated_type(entry) | ljust(12)} | ${entry.applied_visibility}
+        % endif
+      % endfor
+      % if gotEntries:
+    ${path_name(sec) | csym}_END,
+
+      % endif
+    %endfor
+};
+
+/**
+ * Enumeration definitions for the various entries that need them
+ */
+% for sec in find_all_sections(metadata):
+  % for entry in filter_added_in_hal_version(remove_synthetic(find_unique_entries(sec)), hal_major_version(), hal_minor_version()):
+    % if entry.enum:
+// ${entry.name}
+enum CameraMetadataEnum${entry.name | pascal_case} : uint32_t {
+      % for val in entry.enum.values:
+        % if val.id is None:
+    ${entry.name | csym}_${val.name},
+        % else:
+    ${'%s_%s'%(csym(entry.name), val.name) | pad(65)} = ${val.id},
+        % endif
+      % endfor
+};
+
+    % endif
+  % endfor
+% endfor
diff --git a/camera/docs/camera_metadata_tags.mako b/camera/docs/camera_metadata_tags.mako
index 6860385..9351d26 100644
--- a/camera/docs/camera_metadata_tags.mako
+++ b/camera/docs/camera_metadata_tags.mako
@@ -77,10 +77,10 @@
     % for sec in find_all_sections(metadata):
       % for idx,entry in enumerate(remove_synthetic(find_unique_entries(sec))):
         % if idx == 0:
-    ${entry.name + " = " | csym,ljust(50)}// ${annotated_type(entry) | ljust(12)} | ${entry.applied_visibility}
+    ${entry.name + " = " | csym,ljust(50)}// ${annotated_type(entry) | ljust(12)} | ${entry.applied_visibility | ljust(12)} | HIDL v${entry.hal_major_version}.${entry.hal_minor_version}
             ${path_name(find_parent_section(entry)) | csym}_START,
         % else:
-    ${entry.name + "," | csym,ljust(50)}// ${annotated_type(entry) | ljust(12)} | ${entry.applied_visibility | ljust(12)} | HIDL v${entry.hal_version}
+    ${entry.name + "," | csym,ljust(50)}// ${annotated_type(entry) | ljust(12)} | ${entry.applied_visibility | ljust(12)} | HIDL v${entry.hal_major_version}.${entry.hal_minor_version}
         % endif
       % endfor
     ${path_name(sec) | csym}_END,
diff --git a/camera/docs/html.mako b/camera/docs/html.mako
index e63d70a..2074daa 100644
--- a/camera/docs/html.mako
+++ b/camera/docs/html.mako
@@ -346,7 +346,7 @@
             </td>
 
             <td class="entry_hal_version">
-              ${prop.hal_version | md_html, linkify_tags(metadata), wbr}
+              ${"%d.%d" % (prop.hal_major_version, prop.hal_minor_version) | md_html, linkify_tags(metadata), wbr}
             </td>
 
             <td class="entry_tags">
diff --git a/camera/docs/metadata-generate b/camera/docs/metadata-generate
index 27c7811..58b5565 100755
--- a/camera/docs/metadata-generate
+++ b/camera/docs/metadata-generate
@@ -36,6 +36,7 @@
 thisdir=$(cd "$(dirname "$0")"; pwd)
 fwkdir="$ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/camera2/"
 fwkdir_html="$ANDROID_BUILD_TOP/frameworks/base/docs/html/reference"
+hidldir="$ANDROID_BUILD_TOP/hardware/interfaces/camera/metadata"
 ctsdir="$ANDROID_BUILD_TOP/cts/tests/camera/src/android/hardware/camera2/cts"
 outdir="$ANDROID_PRODUCT_OUT/obj/ETC/system-media-camera-docs_intermediates"
 ndk_header_dir="$ANDROID_BUILD_TOP/frameworks/av/camera/ndk/include/camera"
@@ -61,9 +62,10 @@
     local in="$1"
     local out="$2"
     local intermediates="$3"
+    local hal_version="${4:-3.2}"
     local spec_file=$thisdir/metadata_definitions.xml
 
-    python $thisdir/metadata_parser_xml.py $spec_file $in $out
+    python $thisdir/metadata_parser_xml.py $spec_file $in $out $hal_version
 
     local succ=$?
 
@@ -197,6 +199,13 @@
 gen_file camera_metadata_tag_info.mako ../src/camera_metadata_tag_info.c || exit 1
 gen_file camera_metadata_tags.mako ../include/system/camera_metadata_tags.h || exit 1
 
+# Generate HIDL metadata modules - new versions need to be added here manually
+mkdir -p "${hidldir}/3.2"
+gen_file_abs HidlMetadata.mako "$hidldir/3.2/types.hal" yes 3.2 || exit 1
+# Uncomment below to generate 3.3 HIDL module
+#mkdir -p "${hidldir}/3.3"
+#gen_file_abs HidlMetadata.mako "$hidldir/3.3/types.hal" yes 3.3 || exit 1
+
 #Generate NDK header
 gen_file_abs ndk_camera_metadata_tags.mako "$ndk_header_dir/NdkCameraMetadataTags.h" yes || exit 1
 
diff --git a/camera/docs/metadata_helpers.py b/camera/docs/metadata_helpers.py
index c258a4e..7cde49b 100644
--- a/camera/docs/metadata_helpers.py
+++ b/camera/docs/metadata_helpers.py
@@ -37,6 +37,8 @@
 NDKDOC_IMAGE_SRC_METADATA="../" + IMAGE_SRC_METADATA
 
 _context_buf = None
+_hal_major_version = None
+_hal_minor_version = None
 
 def _is_sec_or_ins(x):
   return isinstance(x, metadata_model.Section) or    \
@@ -1256,6 +1258,20 @@
   """
   return (e for e in entries if not e.synthetic)
 
+def filter_added_in_hal_version(entries, hal_major_version, hal_minor_version):
+  """
+  Filter the given entries to those added in the given HIDL HAL version
+
+  Args:
+    entries: An iterable of Entry nodes
+    hal_major_version: Major HIDL version to filter for
+    hal_minor_version: Minor HIDL version to filter for
+
+  Yields:
+    An iterable of Entry nodes
+  """
+  return (e for e in entries if e.hal_major_version == hal_major_version and e.hal_minor_version == hal_minor_version)
+
 def filter_ndk_visible(entries):
   """
   Filter the given entries by removing those that are not NDK visible.
@@ -1327,3 +1343,53 @@
       navigable_string.extract()
 
   return soup.decode()
+
+def hal_major_version():
+  return _hal_major_version
+
+def hal_minor_version():
+  return _hal_minor_version
+
+def first_hal_minor_version(hal_major_version):
+  return 2 if hal_major_version == 3 else 0
+
+def find_all_sections_added_in_hal(root, hal_major_version, hal_minor_version):
+  """
+  Find all descendants that are Section or InnerNamespace instances, which
+  were added in HIDL HAL version major.minor. The section is defined to be
+  added in a HAL version iff the lowest HAL version number of its entries is
+  that HAL version.
+
+  Args:
+    root: a Metadata instance
+    hal_major/minor_version: HAL version numbers
+
+  Returns:
+    A list of Section/InnerNamespace instances
+
+  Remarks:
+    These are known as "sections" in the generated C code.
+  """
+  all_sections = find_all_sections(root)
+  new_sections = []
+  for section in all_sections:
+    min_major_version = None
+    min_minor_version = None
+    for entry in remove_synthetic(find_unique_entries(section)):
+      min_major_version = (min_major_version or entry.hal_major_version)
+      min_minor_version = (min_minor_version or entry.hal_minor_version)
+      if entry.hal_major_version < min_major_version or \
+          (entry.hal_major_version == min_major_version and entry.hal_minor_version < min_minor_version):
+        min_minor_version = entry.hal_minor_version
+        min_major_version = entry.hal_major_version
+    if min_major_version == hal_major_version and min_minor_version == hal_minor_version:
+      new_sections.append(section)
+  return new_sections
+
+def find_first_older_used_hal_version(section, hal_major_version, hal_minor_version):
+  hal_version = (0, 0)
+  for v in section.hal_versions:
+    if (v[0] > hal_version[0] or (v[0] == hal_version[0] and v[1] > hal_version[1])) and \
+        (v[0] < hal_major_version or (v[0] == hal_major_version and v[1] < hal_minor_version)):
+      hal_version = v
+  return hal_version
diff --git a/camera/docs/metadata_model.py b/camera/docs/metadata_model.py
index 66a734d..648e44b 100644
--- a/camera/docs/metadata_model.py
+++ b/camera/docs/metadata_model.py
@@ -690,6 +690,7 @@
     kinds: A sequence of Kind children.
     merged_kinds: A sequence of virtual Kind children,
                   with each Kind's children merged by the kind.name
+    hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
   """
   def __init__(self, name, parent, description=None, kinds=[]):
     self._name = name
@@ -699,7 +700,6 @@
 
     self._leafs = []
 
-
   @property
   def description(self):
     return self._description
@@ -708,6 +708,16 @@
   def kinds(self):
     return (i for i in self._kinds)
 
+  @property
+  def hal_versions(self):
+    hal_versions = set()
+    for i in self._kinds:
+      for entry in i.entries:
+        hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
+      for namespace in i.namespaces:
+        hal_versions.update(namespace.hal_versions)
+    return hal_versions
+
   def sort_children(self):
     self.validate_tree()
     # order is always controls,static,dynamic
@@ -884,6 +894,7 @@
     namespaces: A sequence of InnerNamespace children.
     entries: A sequence of Entry/Clone children.
     merged_entries: A sequence of MergedEntry virtual nodes from entries
+    hal_versions: A set of tuples (major, minor) describing all the HAL versions entries in this section have
   """
   def __init__(self, name, parent):
     self._name        = name
@@ -901,6 +912,15 @@
     return self._entries
 
   @property
+  def hal_versions(self):
+    hal_versions = set()
+    for entry in self.entries:
+      hal_versions.add( (entry.hal_major_version, entry.hal_minor_version) )
+    for namespace in self.namespaces:
+      hal_versions.update(namespace.hal_versions)
+    return hal_versions
+
+  @property
   def merged_entries(self):
     for i in self.entries:
       yield i.merge()
@@ -1170,8 +1190,12 @@
     return self._kind
 
   @property
-  def hal_version(self):
-    return self._hal_version
+  def hal_major_version(self):
+    return self._hal_major_version
+
+  @property
+  def hal_minor_version(self):
+    return self._hal_minor_version
 
   @property
   def visibility(self):
@@ -1300,9 +1324,13 @@
     self._container = kwargs.get('container')
     self._container_sizes = kwargs.get('container_sizes')
 
-    self._hal_version = kwargs.get('hal_version')
-    if self._hal_version is None:
-      self._hal_version = '3.2'
+    hal_version = kwargs.get('hal_version')
+    if hal_version is None:
+      self._hal_major_version = 3
+      self._hal_minor_version = 2
+    else:
+      self._hal_major_version = int(hal_version.partition('.')[0])
+      self._hal_minor_version = int(hal_version.partition('.')[2])
 
     # access these via the 'enum' prop
     enum_values = kwargs.get('enum_values')
@@ -1547,7 +1575,8 @@
                     'deprecated',
                     'optional',
                     'typedef',
-                    'hal_version'
+                    'hal_major_version',
+                    'hal_minor_version'
                    ]
 
     for p in props_common:
diff --git a/camera/docs/metadata_parser_xml.py b/camera/docs/metadata_parser_xml.py
index 0f18e6e..81a7ec9 100755
--- a/camera/docs/metadata_parser_xml.py
+++ b/camera/docs/metadata_parser_xml.py
@@ -303,7 +303,7 @@
 
     return d
 
-  def render(self, template, output_name=None):
+  def render(self, template, output_name=None, hal_version="3.2"):
     """
     Render the metadata model using a Mako template as the view.
 
@@ -315,9 +315,13 @@
     Args:
       template: path to a Mako template file
       output_name: path to the output file, or None to use stdout
+      hal_version: target HAL version, used when generating HIDL HAL outputs.
+                   Must be a string of form "X.Y" where X and Y are integers.
     """
     buf = StringIO.StringIO()
     metadata_helpers._context_buf = buf
+    metadata_helpers._hal_major_version = int(hal_version.partition('.')[0])
+    metadata_helpers._hal_minor_version = int(hal_version.partition('.')[2])
 
     helpers = [(i, getattr(metadata_helpers, i))
                 for i in dir(metadata_helpers) if not i.startswith('_')]
@@ -344,14 +348,16 @@
 if __name__ == "__main__":
   if len(sys.argv) <= 2:
     print >> sys.stderr,                                                       \
-           "Usage: %s <filename.xml> <template.mako> [<output_file>]"          \
+           "Usage: %s <filename.xml> <template.mako> [<output_file>] [<hal_version>]"          \
            % (sys.argv[0])
     sys.exit(0)
 
   file_name = sys.argv[1]
   template_name = sys.argv[2]
   output_name = sys.argv[3] if len(sys.argv) > 3 else None
+  hal_version = sys.argv[4] if len(sys.argv) > 4 else "3.2"
+
   parser = MetadataParserXml.create_from_file(file_name)
-  parser.render(template_name, output_name)
+  parser.render(template_name, output_name, hal_version)
 
   sys.exit(0)
diff --git a/camera/docs/metadata_template.mako b/camera/docs/metadata_template.mako
index 03cd479..f45bfba 100644
--- a/camera/docs/metadata_template.mako
+++ b/camera/docs/metadata_template.mako
@@ -120,8 +120,8 @@
                 hwlevel="${prop.hwlevel}"
           % endif
 
-          % if prop.hal_version != '3.2':
-                hal_version="${prop.hal_version}"
+          % if (prop.hal_major_version, prop.hal_minor_version) != (3,2):
+                hal_version="${prop.hal_major_version}.${prop.hal_minor_version}"
           % endif
             >