Camera: Update SDK metadata key/enum generation
- Flatten Key hierarchy
- Insert Keys into their respective containers instead of separate files
- Use ints instead of Enum types
- Insert enum ints into CameraMetadata
- Add @see cross-references between enum Keys and values.
- Add Javadoc to Keys and enum values.
- Map SCALER_AVAILABLE_FORMATS to int
Bug: 10345522
Change-Id: Ia9762b326b404c572de97c2c7814c4e2e0f3070d
diff --git a/camera/docs/.gitignore b/camera/docs/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/camera/docs/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/camera/docs/CameraMetadataEnums.mako b/camera/docs/CameraMetadataEnums.mako
new file mode 100644
index 0000000..831d61d
--- /dev/null
+++ b/camera/docs/CameraMetadataEnums.mako
@@ -0,0 +1,89 @@
+## -*- coding: utf-8 -*-
+##
+## Copyright (C) 2013 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 section of enum integer definitions is inserted into
+## android.hardware.camera2.CameraMetadata.
+ /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
+ * The enum values below this point are generated from metadata
+ * definitions in /system/media/camera/docs. Do not modify by hand or
+ * modify the comment blocks at the start or end.
+ *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~*/
+##
+## Generate an enum's integers
+<%def name="generate_enum(entry, target_class)">\
+ //
+ // Enumeration values for ${target_class}#${entry.name | jkey_identifier}
+ //
+
+ % for value in entry.enum.values:
+ /**
+ % if value.notes:
+${value.notes | javadoc}\
+ % endif
+ * @see ${target_class}#${entry.name | jkey_identifier}
+ % if entry.applied_visibility == 'hidden':
+ * @hide
+ %endif
+ */
+ public static final int ${jenum_value(entry, value)} = ${enum_calculate_value_string(value)};
+
+ % endfor
+</%def>\
+##
+## Generate a list of only Static, Controls, or Dynamic properties.
+<%def name="single_kind_keys(xml_name, target_class)">\
+% for outer_namespace in metadata.outer_namespaces: ## assumes single 'android' namespace
+ % for section in outer_namespace.sections:
+ % if section.find_first(lambda x: isinstance(x, metadata_model.Entry) and x.kind == xml_name) and \
+ any_visible(section, xml_name, ('public','hidden') ):
+ % for inner_namespace in get_children_by_filtering_kind(section, xml_name, 'namespaces'):
+## We only support 1 level of inner namespace, i.e. android.a.b and android.a.b.c works, but not android.a.b.c.d
+## If we need to support more, we should use a recursive function here instead.. but the indentation gets trickier.
+ % for entry in filter_visibility(inner_namespace.entries, ('hidden','public')):
+ % if entry.enum \
+ and not (entry.typedef and entry.typedef.languages.get('java')) \
+ and not entry.is_clone():
+${generate_enum(entry, target_class)}\
+ % endif
+ % endfor
+ % endfor
+ % for entry in filter_visibility( \
+ get_children_by_filtering_kind(section, xml_name, 'entries'), \
+ ('hidden', 'public')):
+ % if entry.enum \
+ and not (entry.typedef and entry.typedef.languages.get('java')) \
+ and not entry.is_clone():
+${generate_enum(entry, target_class)}\
+ % endif
+ % endfor
+ % endif
+ % endfor
+% endfor
+</%def>\
+
+##
+## Static properties only
+${single_kind_keys('static','CameraProperties')}\
+##
+## Controls properties only
+${single_kind_keys('controls','CaptureRequest')}\
+##
+## Dynamic properties only
+${single_kind_keys('dynamic','CaptureResult')}\
+ /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
+ * End generated code
+ *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/camera/docs/CameraMetadataKeys.mako b/camera/docs/CameraMetadataKeys.mako
index 0ba54b3..a390431 100644
--- a/camera/docs/CameraMetadataKeys.mako
+++ b/camera/docs/CameraMetadataKeys.mako
@@ -1,148 +1,74 @@
## -*- coding: utf-8 -*-
-/*
- * Copyright (C) 2013 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.
- */
-
-package android.hardware.camera2;
-
-import static android.hardware.camera2.CameraMetadata.Key;
-
-/**
- * ! Do not edit this file directly !
- *
- * Generated automatically from ${java_class}Keys.mako
- *
- * TODO: Include a hash of the input files here that the build can check.
- */
-
-<%page args="java_class, xml_kind" />\
-/**
- * The base class for camera controls and information.
- *
- * This class defines the basic key/value map used for querying for camera
- * characteristics or capture results, and for setting camera request
- * parameters.
- *
- * @see ${java_class}
- * @see CameraMetadata
- * @hide
- **/
##
-## Function to generate an enum
-<%def name="generate_enum(entry)">
- % if entry.applied_visibility == 'hidden':
- /**
- * @hide
- */
- %endif
- public static final class ${entry.get_name_minimal() | pascal_case}Key extends Key<${jtype_boxed(entry)}> {
- public enum Enum {
- % for value,last in enumerate_with_last(entry.enum.values):
- ${value.name | jidentifier}${"," if not last else ";"}
- % endfor
- }
+## Copyright (C) 2013 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.
+##
+\
+## These sections of metadata Key definitions are inserted into the middle of
+## android.hardware.camera2.CameraProperties, CaptureRequest, and CaptureResult.
+<%page args="java_class, xml_kind" />\
+ /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
+ * The key entries below this point are generated from metadata
+ * definitions in /system/media/camera/docs. Do not modify by hand or
+ * modify the comment blocks at the start or end.
+ *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~*/
- % for value in entry.enum.values:
- public static final Enum ${value.name | jidentifier} = Enum.${value.name | jidentifier};
- % endfor
-
- // TODO: remove requirement for constructor by making Key an interface
- private ${entry.get_name_minimal() | pascal_case}Key(String name) {
- super(name, ${jtype_boxed(entry)}.class);
- }
-
- % if entry.enum.has_values_with_id:
- static {
- CameraMetadata.registerEnumValues(${jenum(entry.enum)}.class, new int[] {
- % for (value, last) in enumerate_with_last(entry.enum.values):
- ${enum_calculate_value_string(value)}${"," if not last else ""} // ${value.name | jidentifier}
- % endfor
- });
- }
- % endif
- }
+##
+## Generate a single key and docs
+<%def name="generate_key(entry)">\
+ /**
+ % if entry.description:
+${entry.description | javadoc}\
+ % endif
+ % if entry.notes:
+${entry.notes | javadoc}\
+ % endif
+ % if entry.enum and not (entry.typedef and entry.typedef.languages.get('java')):
+ % for value in entry.enum.values:
+ * @see #${jenum_value(entry, value)}
+ % endfor
+ % endif
+ % if entry.applied_visibility == 'hidden':
+ *
+ * @hide
+ % endif
+ */
+ public static final Key<${jtype_boxed(entry)}> ${entry.name | jkey_identifier} =
+ new Key<${jtype_boxed(entry)}>("${entry.name}", ${jclass(entry)});
</%def>\
##
## Generate a list of only Static, Controls, or Dynamic properties.
<%def name="single_kind_keys(java_name, xml_name)">\
-public final class ${java_name}Keys {
% for outer_namespace in metadata.outer_namespaces: ## assumes single 'android' namespace
% for section in outer_namespace.sections:
% if section.find_first(lambda x: isinstance(x, metadata_model.Entry) and x.kind == xml_name) and \
any_visible(section, xml_name, ('public','hidden') ):
- % if not any_visible(section, xml_name, ('public')):
- /**
- * @hide
- */
- %endif
- public static final class ${section.name | pascal_case} {
% for inner_namespace in get_children_by_filtering_kind(section, xml_name, 'namespaces'):
## We only support 1 level of inner namespace, i.e. android.a.b and android.a.b.c works, but not android.a.b.c.d
## If we need to support more, we should use a recursive function here instead.. but the indentation gets trickier.
- public static final class ${inner_namespace.name| pascal_case} {
- % for entry in filter_visibility(inner_namespace.merged_entries, ('hidden','public')):
- % if entry.enum and not (entry.typedef and not entry.typedef.languages.get('java')):
-${generate_enum(entry)}
- % if entry.applied_visibility == 'hidden':
- /**
- * @hide
- */
- %endif
- public static final Key<${jtype_boxed(entry)}> ${entry.get_name_minimal() | csym} =
- new ${entry.get_name_minimal() | pascal_case}Key("${entry.name}");
- % else:
- % if entry.applied_visibility == 'hidden':
- /**
- * @hide
- */
- %endif
- public static final Key<${jtype_boxed(entry)}> ${entry.get_name_minimal() | csym} =
- new Key<${jtype_boxed(entry)}>("${entry.name}", ${jclass(entry)});
- % endif
- % endfor
- }
- % endfor
- % for entry in filter_visibility( \
- get_children_by_filtering_kind(section, xml_name, 'merged_entries'), \
+ % for entry in filter_visibility(inner_namespace.merged_entries, ('hidden','public')):
+${generate_key(entry)}
+ % endfor
+ % endfor
+ % for entry in filter_visibility( \
+ get_children_by_filtering_kind(section, xml_name, 'merged_entries'), \
('hidden', 'public')):
- % if entry.enum and not (entry.typedef and entry.typedef.languages.get('java')):
-${generate_enum(entry)}
- % if entry.applied_visibility == 'hidden':
- /**
- * @hide
- */
- %endif
- public static final Key<${jtype_boxed(entry)}> ${entry.get_name_minimal() | csym} =
- new ${entry.get_name_minimal() | pascal_case}Key("${entry.name}");
- % else:
- % if entry.applied_visibility == 'hidden':
- /**
- * @hide
- */
- %endif
- public static final Key<${jtype_boxed(entry)}> ${entry.get_name_minimal() | csym} =
- new Key<${jtype_boxed(entry)}>("${entry.name}", ${jclass(entry)});
- % endif
- % endfor
-
- }
-
+${generate_key(entry)}
+ % endfor
% endif
% endfor
% endfor
-}
</%def>\
##
## Static properties only
@@ -153,4 +79,7 @@
##
## Dynamic properties only
##${single_kind_keys('CaptureResultKeys', 'dynamic')}
-${single_kind_keys(java_class, xml_kind)}
+${single_kind_keys(java_class, xml_kind)}\
+ /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
+ * End generated code
+ *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
\ No newline at end of file
diff --git a/camera/docs/CameraPropertiesTest.mako b/camera/docs/CameraPropertiesTest.mako
index 88a4ce4..1e46cf6 100644
--- a/camera/docs/CameraPropertiesTest.mako
+++ b/camera/docs/CameraPropertiesTest.mako
@@ -29,7 +29,6 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraProperties;
-import android.hardware.camera2.CameraPropertiesKeys;
import android.test.AndroidTestCase;
/**
@@ -73,7 +72,7 @@
assertNotNull(String.format("Can't get camera properties from: ID %s", ids[i]),
props);
assertNotNull("Invalid property: ${entry.name}",
- props.get(CameraPropertiesKeys.${jkey_identifier(entry.name)}));
+ props.get(CameraProperties.${jkey_identifier(entry.name)}));
}
}
% endif
diff --git a/camera/docs/docs.html b/camera/docs/docs.html
index 49d2e3c..7760aa8 100644
--- a/camera/docs/docs.html
+++ b/camera/docs/docs.html
@@ -7210,7 +7210,7 @@
<span class="entry_type_array">
n
</span>
- <span class="entry_type_visibility"> [public]</span>
+ <span class="entry_type_visibility"> [public as imageFormat]</span>
<div class="entry_type_notes">values from HAL_<wbr>PIXEL_<wbr>FORMAT_<wbr>* in /<wbr>system/<wbr>core/<wbr>include/<wbr>system/<wbr>graphics.<wbr>h</div>
<ul class="entry_type_enum">
diff --git a/camera/docs/metadata-generate b/camera/docs/metadata-generate
index 36df140..9b17b2c 100755
--- a/camera/docs/metadata-generate
+++ b/camera/docs/metadata-generate
@@ -31,7 +31,7 @@
thisdir=$(cd "$(dirname "$0")"; pwd)
fwkdir="$ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/camera2/"
ctsdir="$ANDROID_BUILD_TOP/cts/tests/tests/hardware/src/android/hardware/camera2/cts"
-
+outdir="$ANDROID_PRODUCT_OUT/obj/ETC/system-media-camera-docs_intermediates"
out_files=()
function relpath() {
@@ -50,6 +50,7 @@
function gen_file_abs() {
local in="$1"
local out="$2"
+ local intermediates="$3"
python $thisdir/metadata_parser_xml.py $thisdir/metadata_properties.xml $in $out
@@ -58,7 +59,9 @@
if [[ $succ -eq 0 ]]
then
echo "OK: Generated $(relpath "$out")"
- out_files+=$'\n'" $out"
+ if [[ "$intermediates" != "no" ]]; then
+ out_files+=$'\n'" $out"
+ fi
else
echo "FAIL: Errors while generating $(relpath "$out")" >& 2
fi
@@ -96,15 +99,80 @@
printf %s\\n "${git_directories[@]}" | sort | uniq
}
+# Insert a file into the middle of another, starting at the line containing the
+# start delim and ending on the end delim, both of which are replaced
+function insert_file() {
+ local src_part="$1"
+ local dst_file="$2"
+ local start_delim="/*@O~"
+ local end_delim="~O@*/"
+
+ local start_line="$(grep -n -F "${start_delim}" "${dst_file}" | cut -d: -f1)"
+ local end_line="$(grep -n -F "${end_delim}" "${dst_file}" | cut -d: -f1)"
+
+ # Adjust cutoff points to use start/end line from inserted file
+ (( start_line-- ))
+ (( end_line++ ))
+
+ # Do some basic sanity checks
+
+ if [[ -z "$start_line" ]]; then
+ echo "No starting delimiter found in ${dst_file}" >& 2
+ echo "FAIL: Errors in inserting into $(relpath ${dst_file})" >& 2
+ return 1
+ fi
+
+ if [[ -z "$end_line" ]]; then
+ echo "No ending delimiter found in ${dst_file}" >& 2
+ echo "FAIL: Errors in inserting into $(relpath ${dst_file})" >& 2
+ return 1
+ fi
+
+ if [[ "$start_line" -ge "$end_line" ]]; then
+ echo "Starting delim later than ending delim: $start_line vs $end_line" >& 2
+ echo "FAIL: Errors in inserting into $(relpath ${dst_file})" >& 2
+ return 1
+ fi
+
+ local tmp_name=$(mktemp -t XXXXXXXX)
+
+ # Compose the three parts of the final file together
+
+ head -n "$start_line" "${dst_file}" > "${tmp_name}"
+ cat "${src_part}" >> "${tmp_name}"
+ tail -n "+${end_line}" "${dst_file}" >> "${tmp_name}"
+
+ # And replace the destination file with the new version
+
+ mv "${tmp_name}" "${dst_file}"
+ echo "OK: Inserted $(relpath "$src_part") into $(relpath "$dst_file")"
+ out_files+=$'\n'" $dst_file"
+}
+
$thisdir/metadata-check-dependencies || exit 1
$thisdir/metadata-validate $thisdir/metadata_properties.xml || exit 1
$thisdir/metadata-parser-sanity-check || exit 1
+
+# Generate HTML properties documentation
gen_file html.mako docs.html || exit 1
+
+# Generate C API headers and implementation
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
-gen_file_abs CaptureResultKeys.mako "$fwkdir/CaptureResultKeys.java" || exit 1
-gen_file_abs CaptureRequestKeys.mako "$fwkdir/CaptureRequestKeys.java" || exit 1
-gen_file_abs CameraPropertiesKeys.mako "$fwkdir/CameraPropertiesKeys.java" || exit 1
+
+# Generate Java API definitions
+mkdir -p "${outdir}"
+gen_file_abs CameraMetadataEnums.mako "$outdir/CameraMetadataEnums.java.part" no || exit 1
+gen_file_abs CameraPropertiesKeys.mako "$outdir/CameraPropertiesKeys.java.part" no || exit 1
+gen_file_abs CaptureRequestKeys.mako "$outdir/CaptureRequestKeys.java.part" no || exit 1
+gen_file_abs CaptureResultKeys.mako "$outdir/CaptureResultKeys.java.part" no || exit 1
+
+insert_file "$outdir/CameraMetadataEnums.java.part" "$fwkdir/CameraMetadata.java" || exit 1
+insert_file "$outdir/CameraPropertiesKeys.java.part" "$fwkdir/CameraProperties.java" || exit 1
+insert_file "$outdir/CaptureRequestKeys.java.part" "$fwkdir/CaptureRequest.java" || exit 1
+insert_file "$outdir/CaptureResultKeys.java.part" "$fwkdir/CaptureResult.java" || exit 1
+
+# Generate CTS tests
gen_file_abs CameraPropertiesTest.mako "$ctsdir/CameraPropertiesTest.java" || exit 1
echo ""
diff --git a/camera/docs/metadata_helpers.py b/camera/docs/metadata_helpers.py
index 14bf85c..51444bf 100644
--- a/camera/docs/metadata_helpers.py
+++ b/camera/docs/metadata_helpers.py
@@ -371,11 +371,8 @@
if not java_type:
if not java_type and entry.enum:
- name = entry.name
-
- name_without_ons = entry.get_name_as_list()[1:]
- base_type = ".".join([pascal_case(i) for i in name_without_ons]) + \
- "Key.Enum"
+ # Always map enums to Java ints, unless there's a typedef override
+ base_type = 'int'
else:
mapping = {
@@ -593,29 +590,78 @@
name.
Example:
- jkey_identifier("android.lens.facing") == "Lens.FACING"
+ jkey_identifier("android.lens.facing") == "LENS_FACING"
"""
- tokens = what.split('.')
- return ".".join(map(pascal_case, tokens[1:-1])) + "." + csym(tokens[-1])
+ return csym(what[what.find('.') + 1:])
-def jenum(enum):
+def jenum_value(enum_entry, enum_value):
"""
- Calculate the Java symbol referencing an enum value (in Java).
+ Calculate the Java name for an integer enum value
Args:
- enum: An Enum node
+ enum: An enum-typed Entry node
+ value: An EnumValue node for the enum
Returns:
String representing the Java symbol
"""
- entry = enum.parent
- name = entry.name
+ cname = csym(enum_entry.name)
+ return cname[cname.find('_') + 1:] + '_' + enum_value.name
- name_without_ons = entry.get_name_as_list()[1:]
- jenum_name = ".".join([pascal_case(i) for i in name_without_ons]) + "Key.Enum"
+def javadoc(text, indent = 4):
+ """
+ Format text block as a javadoc comment section
- return jenum_name
+ Args:
+ text: A multi-line string to format
+ indent: baseline level of indentation for javadoc block
+ Returns:
+ String with:
+ - Indent and * for insertion into a Javadoc comment block
+ - Leading/trailing whitespace removed
+ - Paragraph tags added on newlines between paragraphs
+
+ Example:
+ "This is a comment for Javadoc\n" +
+ " with multiple lines, that should be \n" +
+ " formatted better\n" +
+ "\n" +
+ " That covers multiple lines as well\n"
+
+ transforms to
+ " * <p>\n" +
+ " * This is a comment for Javadoc\n" +
+ " * with multiple lines, that should be\n" +
+ " * formatted better\n" +
+ " * </p><p>\n" +
+ " * That covers multiple lines as well\n" +
+ " * </p>\n"
+ """
+ comment_prefix = " " * indent + " * ";
+ comment_para = comment_prefix + "</p><p>\n";
+ javatext = comment_prefix + "<p>\n";
+
+ in_body = False # Eat empty lines at start
+ first_paragraph = True
+ for line in ( line.strip() for line in text.splitlines() ):
+ if not line:
+ in_body = False # collapse multi-blank lines into one
+ else:
+ # Insert para end/start after a span of blank lines except for
+ # the first paragraph, which got a para start already
+ if not in_body and not first_paragraph:
+ javatext = javatext + comment_para
+
+ in_body = True
+ first_paragraph = False
+
+ javatext = javatext + comment_prefix + line + "\n";
+
+ # Close last para tag
+ javatext = javatext + comment_prefix + "</p>\n";
+
+ return javatext
def any_visible(section, kind_name, visibilities):
"""
diff --git a/camera/docs/metadata_properties.xml b/camera/docs/metadata_properties.xml
index 31b07cc..c8575ec 100644
--- a/camera/docs/metadata_properties.xml
+++ b/camera/docs/metadata_properties.xml
@@ -54,6 +54,9 @@
<typedef name="boolean">
<language name="java">boolean</language>
</typedef>
+ <typedef name="imageFormat">
+ <language name="java">int</language>
+ </typedef>
</types>
<namespace name="android">
@@ -1635,7 +1638,7 @@
<entry name="availableFormats" type="int32"
visibility="public" enum="true"
type_notes="values from HAL_PIXEL_FORMAT_* in /system/core/include/system/graphics.h"
- container="array">
+ container="array" typedef="imageFormat">
<array>
<size>n</size>
</array>