[libcamera-devel,4/5] android: Add camera metadata library

Message ID 20190801155420.24694-5-jacopo@jmondi.org
State Superseded
Headers show
Series
  • android: Add initial Camera HAL implementation
Related show

Commit Message

Jacopo Mondi Aug. 1, 2019, 3:54 p.m. UTC
Import the Android camera metadata library from the ChromiumOS build
system.

The camera metadata library has been copied from
https://chromium.googlesource.com/chromiumos/platform2
at revision ceb477360a8012ee38e80746258f4828aad6b4c7.

The original path in the Cros platform2/ repository is:
camera/android/libcamera_metadata/src

Create a new build target for the camera metadata library to
compile the libcamera Android HAL implementation against it.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
---
 src/android/meson.build                       |   10 +
 src/android/metadata/camera_metadata.c        | 1204 +++++++
 .../metadata/camera_metadata_tag_info.c       | 2811 +++++++++++++++++
 3 files changed, 4025 insertions(+)
 create mode 100644 src/android/meson.build
 create mode 100644 src/android/metadata/camera_metadata.c
 create mode 100644 src/android/metadata/camera_metadata_tag_info.c

Comments

Laurent Pinchart Aug. 5, 2019, 11:49 a.m. UTC | #1
Hi Jacopo,

Thank you for the patch.

On Thu, Aug 01, 2019 at 05:54:19PM +0200, Jacopo Mondi wrote:
> Import the Android camera metadata library from the ChromiumOS build
> system.
> 
> The camera metadata library has been copied from
> https://chromium.googlesource.com/chromiumos/platform2
> at revision ceb477360a8012ee38e80746258f4828aad6b4c7.
> 
> The original path in the Cros platform2/ repository is:
> camera/android/libcamera_metadata/src
> 
> Create a new build target for the camera metadata library to
> compile the libcamera Android HAL implementation against it.
> 
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> ---
>  src/android/meson.build                       |   10 +
>  src/android/metadata/camera_metadata.c        | 1204 +++++++
>  .../metadata/camera_metadata_tag_info.c       | 2811 +++++++++++++++++
>  3 files changed, 4025 insertions(+)
>  create mode 100644 src/android/meson.build
>  create mode 100644 src/android/metadata/camera_metadata.c
>  create mode 100644 src/android/metadata/camera_metadata_tag_info.c
> 
> diff --git a/src/android/meson.build b/src/android/meson.build
> new file mode 100644
> index 000000000000..e988dfa9ee63
> --- /dev/null
> +++ b/src/android/meson.build
> @@ -0,0 +1,10 @@
> +android_camera_metadata_sources = files([
> +    'metadata/camera_metadata.c',
> +])
> +
> +# Do not install by default as the target systems (Android, ChromeOS) already
> +# ship a libcamera_metadata.so library.

Shouldn't you thus compile it statically (or at least as a static
library) ? If we want to link at runtime against the shared library
provided by the system then we should compile against the corresponding
headers, not against our own copy of the headers. That wouldn't be
practical, so statically linking is likely best.

Longer term I think we should replace this with a custom C++
implementation.

> +android_camera_metadata = shared_library('camera_metadata',
> +                                         android_camera_metadata_sources,
> +                                         install : false,
> +                                         include_directories : android_includes)
> diff --git a/src/android/metadata/camera_metadata.c b/src/android/metadata/camera_metadata.c
> new file mode 100644
> index 000000000000..6bfd02da29c7
> --- /dev/null
> +++ b/src/android/metadata/camera_metadata.c
> @@ -0,0 +1,1204 @@

SPDX here too please (in a separate patch).

What are the implications of linking LGPL-2.1+ and Apache-2.0 code
together ?

> +/*
> + * Copyright (C) 2012 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.
> + */

[snip]
Jacopo Mondi Aug. 6, 2019, 10:38 a.m. UTC | #2
Hi Laurent,

On Mon, Aug 05, 2019 at 02:49:36PM +0300, Laurent Pinchart wrote:
> Hi Jacopo,
>
> Thank you for the patch.
>
> On Thu, Aug 01, 2019 at 05:54:19PM +0200, Jacopo Mondi wrote:
> > Import the Android camera metadata library from the ChromiumOS build
> > system.
> >
> > The camera metadata library has been copied from
> > https://chromium.googlesource.com/chromiumos/platform2
> > at revision ceb477360a8012ee38e80746258f4828aad6b4c7.
> >
> > The original path in the Cros platform2/ repository is:
> > camera/android/libcamera_metadata/src
> >
> > Create a new build target for the camera metadata library to
> > compile the libcamera Android HAL implementation against it.
> >
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > ---
> >  src/android/meson.build                       |   10 +
> >  src/android/metadata/camera_metadata.c        | 1204 +++++++
> >  .../metadata/camera_metadata_tag_info.c       | 2811 +++++++++++++++++
> >  3 files changed, 4025 insertions(+)
> >  create mode 100644 src/android/meson.build
> >  create mode 100644 src/android/metadata/camera_metadata.c
> >  create mode 100644 src/android/metadata/camera_metadata_tag_info.c
> >
> > diff --git a/src/android/meson.build b/src/android/meson.build
> > new file mode 100644
> > index 000000000000..e988dfa9ee63
> > --- /dev/null
> > +++ b/src/android/meson.build
> > @@ -0,0 +1,10 @@
> > +android_camera_metadata_sources = files([
> > +    'metadata/camera_metadata.c',
> > +])
> > +
> > +# Do not install by default as the target systems (Android, ChromeOS) already
> > +# ship a libcamera_metadata.so library.
>
> Shouldn't you thus compile it statically (or at least as a static
> library) ? If we want to link at runtime against the shared library
> provided by the system then we should compile against the corresponding
> headers, not against our own copy of the headers. That wouldn't be
> practical, so statically linking is likely best.
>

Wouldn't this open door to mis-alignements between the metadata
library version we compile libcamera against and the one the camera
framework on the target system provides ? In the same way we might
have a mis-alignemnt of headers version when linking dynamically, we
would have the same if we statically link our version of the metadata
library.. This is tricky to solve, as we might then need to
ship/include different version of this library depending which is the
target system the HAL is compiled for... As of now, as we only target CrOS,
keeping in sync headers and the library from the cros sources and here
is not hard, but what when Android support will be added?

For now, with a single system supported, if we make sure headers and
the metadata library implementation are in sync between libcamera and
the cros sources, I guess statically linking would only give us a tad
fatter library without any additional guarantees.

> Longer term I think we should replace this with a custom C++
> implementation.
>
> > +android_camera_metadata = shared_library('camera_metadata',
> > +                                         android_camera_metadata_sources,
> > +                                         install : false,
> > +                                         include_directories : android_includes)
> > diff --git a/src/android/metadata/camera_metadata.c b/src/android/metadata/camera_metadata.c
> > new file mode 100644
> > index 000000000000..6bfd02da29c7
> > --- /dev/null
> > +++ b/src/android/metadata/camera_metadata.c
> > @@ -0,0 +1,1204 @@
>
> SPDX here too please (in a separate patch).
>
> What are the implications of linking LGPL-2.1+ and Apache-2.0 code
> together ?
>

Eh, how fun is to deal with legal stuff..

So, not need to say but IANAL and none of us is, so the best source I
could find is this stackoverflow post
https://opensource.stackexchange.com/questions/5664/linking-from-lgpl-2-1-software-to-apache-2-0-library

The general question we are trying to answer is:

    Can copyleft-licensed code depend on non-copyleft-licensed code that
    is using a license that is deemed incompatible with a given copyleft
    license?

Going through the answers and the FAQs on the GPL license website, it
seems the me the one that applies the most to our use case is:


        What legal issues come up if I use GPL-incompatible libraries with
        GPL software? If you want your program to link against a library
        not covered by the system library exception, you need to provide
        permission to do that.

        So even though the LGPL is not the GPL, I would say that the best way
        to do this would be to follow the guidelines provided at the FAQ and
        license your LGPL library with an exception stating that this does not
        extend to the Apache-licensed dependencies. Something similar to
        OpenSSL exceptions commonly found in several places such as here.

That's because I'm not sure I found a proper definition of "system
library exception" for LGPL-2.1. If the metadata library falls under
that term, we should be good the way we are, otherwise we would need
to add a notice.

> > +/*
> > + * Copyright (C) 2012 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.
> > + */
>
> [snip]
>
> --
> Regards,
>
> Laurent Pinchart
Laurent Pinchart Aug. 6, 2019, 1:22 p.m. UTC | #3
Hi Jacopo,

On Tue, Aug 06, 2019 at 12:38:04PM +0200, Jacopo Mondi wrote:
> On Mon, Aug 05, 2019 at 02:49:36PM +0300, Laurent Pinchart wrote:
> > On Thu, Aug 01, 2019 at 05:54:19PM +0200, Jacopo Mondi wrote:
> > > Import the Android camera metadata library from the ChromiumOS build
> > > system.
> > >
> > > The camera metadata library has been copied from
> > > https://chromium.googlesource.com/chromiumos/platform2
> > > at revision ceb477360a8012ee38e80746258f4828aad6b4c7.
> > >
> > > The original path in the Cros platform2/ repository is:
> > > camera/android/libcamera_metadata/src
> > >
> > > Create a new build target for the camera metadata library to
> > > compile the libcamera Android HAL implementation against it.
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > > ---
> > >  src/android/meson.build                       |   10 +
> > >  src/android/metadata/camera_metadata.c        | 1204 +++++++
> > >  .../metadata/camera_metadata_tag_info.c       | 2811 +++++++++++++++++
> > >  3 files changed, 4025 insertions(+)
> > >  create mode 100644 src/android/meson.build
> > >  create mode 100644 src/android/metadata/camera_metadata.c
> > >  create mode 100644 src/android/metadata/camera_metadata_tag_info.c
> > >
> > > diff --git a/src/android/meson.build b/src/android/meson.build
> > > new file mode 100644
> > > index 000000000000..e988dfa9ee63
> > > --- /dev/null
> > > +++ b/src/android/meson.build
> > > @@ -0,0 +1,10 @@
> > > +android_camera_metadata_sources = files([
> > > +    'metadata/camera_metadata.c',
> > > +])
> > > +
> > > +# Do not install by default as the target systems (Android, ChromeOS) already
> > > +# ship a libcamera_metadata.so library.
> >
> > Shouldn't you thus compile it statically (or at least as a static
> > library) ? If we want to link at runtime against the shared library
> > provided by the system then we should compile against the corresponding
> > headers, not against our own copy of the headers. That wouldn't be
> > practical, so statically linking is likely best.
> 
> Wouldn't this open door to mis-alignements between the metadata
> library version we compile libcamera against and the one the camera
> framework on the target system provides ? In the same way we might
> have a mis-alignemnt of headers version when linking dynamically, we
> would have the same if we statically link our version of the metadata
> library.. This is tricky to solve, as we might then need to
> ship/include different version of this library depending which is the
> target system the HAL is compiled for... As of now, as we only target CrOS,
> keeping in sync headers and the library from the cros sources and here
> is not hard, but what when Android support will be added?

Are there differences in the data layout between different Android
versions ? I thought the layout was stable (but I could be wrong).

> For now, with a single system supported, if we make sure headers and
> the metadata library implementation are in sync between libcamera and
> the cros sources, I guess statically linking would only give us a tad
> fatter library without any additional guarantees.

Making sure they're in sync is the issue. You should either copy both
the header and sources to the libcamera tree, and compile statically, or
copy none. Especially copying the sources to link dynamically and then
run against a different binary is a bad idea. I have a preference for
copying both for now, but if you prefer copying neither, I'm OK with
that too. We would then need another compilation option to point to the
headers and library to be compiled and linked against.

> > Longer term I think we should replace this with a custom C++
> > implementation.
> >
> > > +android_camera_metadata = shared_library('camera_metadata',
> > > +                                         android_camera_metadata_sources,
> > > +                                         install : false,
> > > +                                         include_directories : android_includes)
> > > diff --git a/src/android/metadata/camera_metadata.c b/src/android/metadata/camera_metadata.c
> > > new file mode 100644
> > > index 000000000000..6bfd02da29c7
> > > --- /dev/null
> > > +++ b/src/android/metadata/camera_metadata.c
> > > @@ -0,0 +1,1204 @@
> >
> > SPDX here too please (in a separate patch).
> >
> > What are the implications of linking LGPL-2.1+ and Apache-2.0 code
> > together ?
> 
> Eh, how fun is to deal with legal stuff..
> 
> So, not need to say but IANAL and none of us is, so the best source I
> could find is this stackoverflow post
> https://opensource.stackexchange.com/questions/5664/linking-from-lgpl-2-1-software-to-apache-2-0-library
> 
> The general question we are trying to answer is:
> 
>     Can copyleft-licensed code depend on non-copyleft-licensed code that
>     is using a license that is deemed incompatible with a given copyleft
>     license?
> 
> Going through the answers and the FAQs on the GPL license website, it
> seems the me the one that applies the most to our use case is:
> 
> 
>         What legal issues come up if I use GPL-incompatible libraries with
>         GPL software? If you want your program to link against a library
>         not covered by the system library exception, you need to provide
>         permission to do that.
> 
>         So even though the LGPL is not the GPL, I would say that the best way
>         to do this would be to follow the guidelines provided at the FAQ and
>         license your LGPL library with an exception stating that this does not
>         extend to the Apache-licensed dependencies. Something similar to
>         OpenSSL exceptions commonly found in several places such as here.
> 
> That's because I'm not sure I found a proper definition of "system
> library exception" for LGPL-2.1. If the metadata library falls under
> that term, we should be good the way we are, otherwise we would need
> to add a notice.

The system library exception mostly covers glibc, and possibly a few
other similar system libraries from the GNU project. The camera metadata
library isn't included.

The GPL and LGPL apply to binary distribution time, so linking
statically against the metadata library is probably an issue, while
linking dynamically is probably safer. I wonder how much this calls for
a custom implementation...

> > > +/*
> > > + * Copyright (C) 2012 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.
> > > + */
> >
> > [snip]
Jacopo Mondi Aug. 6, 2019, 2:26 p.m. UTC | #4
Hi Laurent,

On Tue, Aug 06, 2019 at 04:22:40PM +0300, Laurent Pinchart wrote:
> Hi Jacopo,
>
> On Tue, Aug 06, 2019 at 12:38:04PM +0200, Jacopo Mondi wrote:
> > On Mon, Aug 05, 2019 at 02:49:36PM +0300, Laurent Pinchart wrote:
> > > On Thu, Aug 01, 2019 at 05:54:19PM +0200, Jacopo Mondi wrote:
> > > > Import the Android camera metadata library from the ChromiumOS build
> > > > system.
> > > >
> > > > The camera metadata library has been copied from
> > > > https://chromium.googlesource.com/chromiumos/platform2
> > > > at revision ceb477360a8012ee38e80746258f4828aad6b4c7.
> > > >
> > > > The original path in the Cros platform2/ repository is:
> > > > camera/android/libcamera_metadata/src
> > > >
> > > > Create a new build target for the camera metadata library to
> > > > compile the libcamera Android HAL implementation against it.
> > > >
> > > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > > > ---
> > > >  src/android/meson.build                       |   10 +
> > > >  src/android/metadata/camera_metadata.c        | 1204 +++++++
> > > >  .../metadata/camera_metadata_tag_info.c       | 2811 +++++++++++++++++
> > > >  3 files changed, 4025 insertions(+)
> > > >  create mode 100644 src/android/meson.build
> > > >  create mode 100644 src/android/metadata/camera_metadata.c
> > > >  create mode 100644 src/android/metadata/camera_metadata_tag_info.c
> > > >
> > > > diff --git a/src/android/meson.build b/src/android/meson.build
> > > > new file mode 100644
> > > > index 000000000000..e988dfa9ee63
> > > > --- /dev/null
> > > > +++ b/src/android/meson.build
> > > > @@ -0,0 +1,10 @@
> > > > +android_camera_metadata_sources = files([
> > > > +    'metadata/camera_metadata.c',
> > > > +])
> > > > +
> > > > +# Do not install by default as the target systems (Android, ChromeOS) already
> > > > +# ship a libcamera_metadata.so library.
> > >
> > > Shouldn't you thus compile it statically (or at least as a static
> > > library) ? If we want to link at runtime against the shared library
> > > provided by the system then we should compile against the corresponding
> > > headers, not against our own copy of the headers. That wouldn't be
> > > practical, so statically linking is likely best.
> >
> > Wouldn't this open door to mis-alignements between the metadata
> > library version we compile libcamera against and the one the camera
> > framework on the target system provides ? In the same way we might
> > have a mis-alignemnt of headers version when linking dynamically, we
> > would have the same if we statically link our version of the metadata
> > library.. This is tricky to solve, as we might then need to
> > ship/include different version of this library depending which is the
> > target system the HAL is compiled for... As of now, as we only target CrOS,
> > keeping in sync headers and the library from the cros sources and here
> > is not hard, but what when Android support will be added?
>
> Are there differences in the data layout between different Android
> versions ? I thought the layout was stable (but I could be wrong).

I can't tell, seems unlikely, but still... I was more concerned about
using tags defined in a later library versions than the one running on
the system more than the layout itself...
>
> > For now, with a single system supported, if we make sure headers and
> > the metadata library implementation are in sync between libcamera and
> > the cros sources, I guess statically linking would only give us a tad
> > fatter library without any additional guarantees.
>
> Making sure they're in sync is the issue. You should either copy both
> the header and sources to the libcamera tree, and compile statically, or
> copy none. Especially copying the sources to link dynamically and then
> run against a different binary is a bad idea. I have a preference for
> copying both for now, but if you prefer copying neither, I'm OK with
> that too. We would then need another compilation option to point to the
> headers and library to be compiled and linked against.
>

Ok, I indeed prefer to copy both for now instead of requiring users to
have their own copies on the system and point to them. We'll might
want to change this at a later stage or let the integration in
android/cros system handle this.


> > > Longer term I think we should replace this with a custom C++
> > > implementation.
> > >
> > > > +android_camera_metadata = shared_library('camera_metadata',
> > > > +                                         android_camera_metadata_sources,
> > > > +                                         install : false,
> > > > +                                         include_directories : android_includes)
> > > > diff --git a/src/android/metadata/camera_metadata.c b/src/android/metadata/camera_metadata.c
> > > > new file mode 100644
> > > > index 000000000000..6bfd02da29c7
> > > > --- /dev/null
> > > > +++ b/src/android/metadata/camera_metadata.c
> > > > @@ -0,0 +1,1204 @@
> > >
> > > SPDX here too please (in a separate patch).
> > >
> > > What are the implications of linking LGPL-2.1+ and Apache-2.0 code
> > > together ?
> >
> > Eh, how fun is to deal with legal stuff..
> >
> > So, not need to say but IANAL and none of us is, so the best source I
> > could find is this stackoverflow post
> > https://opensource.stackexchange.com/questions/5664/linking-from-lgpl-2-1-software-to-apache-2-0-library
> >
> > The general question we are trying to answer is:
> >
> >     Can copyleft-licensed code depend on non-copyleft-licensed code that
> >     is using a license that is deemed incompatible with a given copyleft
> >     license?
> >
> > Going through the answers and the FAQs on the GPL license website, it
> > seems the me the one that applies the most to our use case is:
> >
> >
> >         What legal issues come up if I use GPL-incompatible libraries with
> >         GPL software? If you want your program to link against a library
> >         not covered by the system library exception, you need to provide
> >         permission to do that.
> >
> >         So even though the LGPL is not the GPL, I would say that the best way
> >         to do this would be to follow the guidelines provided at the FAQ and
> >         license your LGPL library with an exception stating that this does not
> >         extend to the Apache-licensed dependencies. Something similar to
> >         OpenSSL exceptions commonly found in several places such as here.
> >
> > That's because I'm not sure I found a proper definition of "system
> > library exception" for LGPL-2.1. If the metadata library falls under
> > that term, we should be good the way we are, otherwise we would need
> > to add a notice.
>
> The system library exception mostly covers glibc, and possibly a few
> other similar system libraries from the GNU project. The camera metadata
> library isn't included.

I have guessed so.
>
> The GPL and LGPL apply to binary distribution time, so linking
> statically against the metadata library is probably an issue, while
> linking dynamically is probably safer. I wonder how much this calls for
> a custom implementation...
>

Won't an adding an exception cover the static linking case as well?
I agree that an internal implementation might be desirable and would
save us some license headaches, but it should be implemented and I'm
not sure it is top priority at the moment...


> > > > +/*
> > > > + * Copyright (C) 2012 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.
> > > > + */
> > >
> > > [snip]
>
> --
> Regards,
>
> Laurent Pinchart

Patch

diff --git a/src/android/meson.build b/src/android/meson.build
new file mode 100644
index 000000000000..e988dfa9ee63
--- /dev/null
+++ b/src/android/meson.build
@@ -0,0 +1,10 @@ 
+android_camera_metadata_sources = files([
+    'metadata/camera_metadata.c',
+])
+
+# Do not install by default as the target systems (Android, ChromeOS) already
+# ship a libcamera_metadata.so library.
+android_camera_metadata = shared_library('camera_metadata',
+                                         android_camera_metadata_sources,
+                                         install : false,
+                                         include_directories : android_includes)
diff --git a/src/android/metadata/camera_metadata.c b/src/android/metadata/camera_metadata.c
new file mode 100644
index 000000000000..6bfd02da29c7
--- /dev/null
+++ b/src/android/metadata/camera_metadata.c
@@ -0,0 +1,1204 @@ 
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define LOG_TAG "camera_metadata"
+
+/*
+ * Replace ALOGE() with a fprintf to stderr so that we don't need to
+ * re-implement Android's logging system.  The log/log.h header file is no
+ * longer necessary once we removed dependency on ALOGE().
+ */
+#define ALOGE(...) fprintf(stderr, LOG_TAG __VA_ARGS__)
+
+#include <system/camera_metadata.h>
+#include <camera_metadata_hidden.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stddef.h>  // for offsetof
+#include <stdio.h>
+#include <stdlib.h>
+
+#define OK              0
+#define ERROR           1
+#define NOT_FOUND       (-ENOENT)
+#define SN_EVENT_LOG_ID 0x534e4554
+
+#define ALIGN_TO(val, alignment) \
+    (((uintptr_t)(val) + ((alignment) - 1)) & ~((alignment) - 1))
+
+/**
+ * A single metadata entry, storing an array of values of a given type. If the
+ * array is no larger than 4 bytes in size, it is stored in the data.value[]
+ * array; otherwise, it can found in the parent's data array at index
+ * data.offset.
+ */
+#define ENTRY_ALIGNMENT ((size_t) 4)
+typedef struct camera_metadata_buffer_entry {
+    uint32_t tag;
+    uint32_t count;
+    union {
+        uint32_t offset;
+        uint8_t  value[4];
+    } data;
+    uint8_t  type;
+    uint8_t  reserved[3];
+} camera_metadata_buffer_entry_t;
+
+typedef uint32_t metadata_uptrdiff_t;
+typedef uint32_t metadata_size_t;
+
+/**
+ * A packet of metadata. This is a list of entries, each of which may point to
+ * its values stored at an offset in data.
+ *
+ * It is assumed by the utility functions that the memory layout of the packet
+ * is as follows:
+ *
+ *   |-----------------------------------------------|
+ *   | camera_metadata_t                             |
+ *   |                                               |
+ *   |-----------------------------------------------|
+ *   | reserved for future expansion                 |
+ *   |-----------------------------------------------|
+ *   | camera_metadata_buffer_entry_t #0             |
+ *   |-----------------------------------------------|
+ *   | ....                                          |
+ *   |-----------------------------------------------|
+ *   | camera_metadata_buffer_entry_t #entry_count-1 |
+ *   |-----------------------------------------------|
+ *   | free space for                                |
+ *   | (entry_capacity-entry_count) entries          |
+ *   |-----------------------------------------------|
+ *   | start of camera_metadata.data                 |
+ *   |                                               |
+ *   |-----------------------------------------------|
+ *   | free space for                                |
+ *   | (data_capacity-data_count) bytes              |
+ *   |-----------------------------------------------|
+ *
+ * With the total length of the whole packet being camera_metadata.size bytes.
+ *
+ * In short, the entries and data are contiguous in memory after the metadata
+ * header.
+ */
+#define METADATA_ALIGNMENT ((size_t) 4)
+struct camera_metadata {
+    metadata_size_t          size;
+    uint32_t                 version;
+    uint32_t                 flags;
+    metadata_size_t          entry_count;
+    metadata_size_t          entry_capacity;
+    metadata_uptrdiff_t      entries_start; // Offset from camera_metadata
+    metadata_size_t          data_count;
+    metadata_size_t          data_capacity;
+    metadata_uptrdiff_t      data_start; // Offset from camera_metadata
+    uint32_t                 padding;    // padding to 8 bytes boundary
+    metadata_vendor_id_t     vendor_id;
+};
+
+/**
+ * A datum of metadata. This corresponds to camera_metadata_entry_t::data
+ * with the difference that each element is not a pointer. We need to have a
+ * non-pointer type description in order to figure out the largest alignment
+ * requirement for data (DATA_ALIGNMENT).
+ */
+#define DATA_ALIGNMENT ((size_t) 8)
+typedef union camera_metadata_data {
+    uint8_t u8;
+    int32_t i32;
+    float   f;
+    int64_t i64;
+    double  d;
+    camera_metadata_rational_t r;
+} camera_metadata_data_t;
+
+_Static_assert(sizeof(metadata_size_t) == 4,
+         "Size of metadata_size_t must be 4");
+_Static_assert(sizeof(metadata_uptrdiff_t) == 4,
+         "Size of metadata_uptrdiff_t must be 4");
+_Static_assert(sizeof(metadata_vendor_id_t) == 8,
+         "Size of metadata_vendor_id_t must be 8");
+_Static_assert(sizeof(camera_metadata_data_t) == 8,
+         "Size of camera_metadata_data_t must be 8");
+
+_Static_assert(offsetof(camera_metadata_buffer_entry_t, tag) == 0,
+         "Offset of tag must be 0");
+_Static_assert(offsetof(camera_metadata_buffer_entry_t, count) == 4,
+         "Offset of count must be 4");
+_Static_assert(offsetof(camera_metadata_buffer_entry_t, data) == 8,
+         "Offset of data must be 8");
+_Static_assert(offsetof(camera_metadata_buffer_entry_t, type) == 12,
+         "Offset of type must be 12");
+_Static_assert(sizeof(camera_metadata_buffer_entry_t) == 16,
+         "Size of camera_metadata_buffer_entry_t must be 16");
+
+_Static_assert(offsetof(camera_metadata_t, size) == 0,
+         "Offset of size must be 0");
+_Static_assert(offsetof(camera_metadata_t, version) == 4,
+         "Offset of version must be 4");
+_Static_assert(offsetof(camera_metadata_t, flags) == 8,
+         "Offset of flags must be 8");
+_Static_assert(offsetof(camera_metadata_t, entry_count) == 12,
+         "Offset of entry_count must be 12");
+_Static_assert(offsetof(camera_metadata_t, entry_capacity) == 16,
+         "Offset of entry_capacity must be 16");
+_Static_assert(offsetof(camera_metadata_t, entries_start) == 20,
+         "Offset of entries_start must be 20");
+_Static_assert(offsetof(camera_metadata_t, data_count) == 24,
+         "Offset of data_count must be 24");
+_Static_assert(offsetof(camera_metadata_t, data_capacity) == 28,
+         "Offset of data_capacity must be 28");
+_Static_assert(offsetof(camera_metadata_t, data_start) == 32,
+         "Offset of data_start must be 32");
+_Static_assert(offsetof(camera_metadata_t, vendor_id) == 40,
+         "Offset of vendor_id must be 40");
+_Static_assert(sizeof(camera_metadata_t) == 48,
+         "Size of camera_metadata_t must be 48");
+
+/**
+ * The preferred alignment of a packet of camera metadata. In general,
+ * this is the lowest common multiple of the constituents of a metadata
+ * package, i.e, of DATA_ALIGNMENT and ENTRY_ALIGNMENT.
+ */
+#define MAX_ALIGNMENT(A, B) (((A) > (B)) ? (A) : (B))
+#define METADATA_PACKET_ALIGNMENT \
+    MAX_ALIGNMENT(MAX_ALIGNMENT(DATA_ALIGNMENT, METADATA_ALIGNMENT), ENTRY_ALIGNMENT)
+
+/** Versioning information */
+#define CURRENT_METADATA_VERSION 1
+
+/** Flag definitions */
+#define FLAG_SORTED 0x00000001
+
+/** Tag information */
+
+typedef struct tag_info {
+    const char *tag_name;
+    uint8_t     tag_type;
+} tag_info_t;
+
+#include "camera_metadata_tag_info.c"
+
+const size_t camera_metadata_type_size[NUM_TYPES] = {
+    [TYPE_BYTE]     = sizeof(uint8_t),
+    [TYPE_INT32]    = sizeof(int32_t),
+    [TYPE_FLOAT]    = sizeof(float),
+    [TYPE_INT64]    = sizeof(int64_t),
+    [TYPE_DOUBLE]   = sizeof(double),
+    [TYPE_RATIONAL] = sizeof(camera_metadata_rational_t)
+};
+
+const char *camera_metadata_type_names[NUM_TYPES] = {
+    [TYPE_BYTE]     = "byte",
+    [TYPE_INT32]    = "int32",
+    [TYPE_FLOAT]    = "float",
+    [TYPE_INT64]    = "int64",
+    [TYPE_DOUBLE]   = "double",
+    [TYPE_RATIONAL] = "rational"
+};
+
+static camera_metadata_buffer_entry_t *get_entries(
+        const camera_metadata_t *metadata) {
+    return (camera_metadata_buffer_entry_t*)
+            ((uint8_t*)metadata + metadata->entries_start);
+}
+
+static uint8_t *get_data(const camera_metadata_t *metadata) {
+    return (uint8_t*)metadata + metadata->data_start;
+}
+
+size_t get_camera_metadata_alignment() {
+    return METADATA_PACKET_ALIGNMENT;
+}
+
+camera_metadata_t *allocate_copy_camera_metadata_checked(
+        const camera_metadata_t *src,
+        size_t src_size) {
+
+    if (src == NULL) {
+        return NULL;
+    }
+
+    if (src_size < sizeof(camera_metadata_t)) {
+        ALOGE("%s: Source size too small!", __FUNCTION__);
+        // android_errorWriteLog(0x534e4554, "67782345");
+        return NULL;
+    }
+
+    void *buffer = malloc(src_size);
+    memcpy(buffer, src, src_size);
+
+    camera_metadata_t *metadata = (camera_metadata_t*) buffer;
+    if (validate_camera_metadata_structure(metadata, &src_size) != OK) {
+        free(buffer);
+        return NULL;
+    }
+
+    return metadata;
+}
+
+camera_metadata_t *allocate_camera_metadata(size_t entry_capacity,
+                                            size_t data_capacity) {
+
+    size_t memory_needed = calculate_camera_metadata_size(entry_capacity,
+                                                          data_capacity);
+    void *buffer = malloc(memory_needed);
+    camera_metadata_t *metadata = place_camera_metadata(
+        buffer, memory_needed, entry_capacity, data_capacity);
+    if (!metadata) {
+        /* This should not happen when memory_needed is the same
+         * calculated in this function and in place_camera_metadata.
+         */
+        free(buffer);
+    }
+    return metadata;
+}
+
+camera_metadata_t *place_camera_metadata(void *dst,
+                                         size_t dst_size,
+                                         size_t entry_capacity,
+                                         size_t data_capacity) {
+    if (dst == NULL) return NULL;
+
+    size_t memory_needed = calculate_camera_metadata_size(entry_capacity,
+                                                          data_capacity);
+    if (memory_needed > dst_size) return NULL;
+
+    camera_metadata_t *metadata = (camera_metadata_t*)dst;
+    metadata->version = CURRENT_METADATA_VERSION;
+    metadata->flags = 0;
+    metadata->entry_count = 0;
+    metadata->entry_capacity = entry_capacity;
+    metadata->entries_start =
+            ALIGN_TO(sizeof(camera_metadata_t), ENTRY_ALIGNMENT);
+    metadata->data_count = 0;
+    metadata->data_capacity = data_capacity;
+    metadata->size = memory_needed;
+    size_t data_unaligned = (uint8_t*)(get_entries(metadata) +
+            metadata->entry_capacity) - (uint8_t*)metadata;
+    metadata->data_start = ALIGN_TO(data_unaligned, DATA_ALIGNMENT);
+    metadata->vendor_id = CAMERA_METADATA_INVALID_VENDOR_ID;
+
+    assert(validate_camera_metadata_structure(metadata, NULL) == OK);
+    return metadata;
+}
+void free_camera_metadata(camera_metadata_t *metadata) {
+    free(metadata);
+}
+
+size_t calculate_camera_metadata_size(size_t entry_count,
+                                      size_t data_count) {
+    size_t memory_needed = sizeof(camera_metadata_t);
+    // Start entry list at aligned boundary
+    memory_needed = ALIGN_TO(memory_needed, ENTRY_ALIGNMENT);
+    memory_needed += sizeof(camera_metadata_buffer_entry_t[entry_count]);
+    // Start buffer list at aligned boundary
+    memory_needed = ALIGN_TO(memory_needed, DATA_ALIGNMENT);
+    memory_needed += sizeof(uint8_t[data_count]);
+    // Make sure camera metadata can be stacked in continuous memory
+    memory_needed = ALIGN_TO(memory_needed, METADATA_PACKET_ALIGNMENT);
+    return memory_needed;
+}
+
+size_t get_camera_metadata_size(const camera_metadata_t *metadata) {
+    if (metadata == NULL) return ERROR;
+
+    return metadata->size;
+}
+
+size_t get_camera_metadata_compact_size(const camera_metadata_t *metadata) {
+    if (metadata == NULL) return ERROR;
+
+    return calculate_camera_metadata_size(metadata->entry_count,
+                                          metadata->data_count);
+}
+
+size_t get_camera_metadata_entry_count(const camera_metadata_t *metadata) {
+    return metadata->entry_count;
+}
+
+size_t get_camera_metadata_entry_capacity(const camera_metadata_t *metadata) {
+    return metadata->entry_capacity;
+}
+
+size_t get_camera_metadata_data_count(const camera_metadata_t *metadata) {
+    return metadata->data_count;
+}
+
+size_t get_camera_metadata_data_capacity(const camera_metadata_t *metadata) {
+    return metadata->data_capacity;
+}
+
+camera_metadata_t* copy_camera_metadata(void *dst, size_t dst_size,
+        const camera_metadata_t *src) {
+    size_t memory_needed = get_camera_metadata_compact_size(src);
+
+    if (dst == NULL) return NULL;
+    if (dst_size < memory_needed) return NULL;
+
+    camera_metadata_t *metadata =
+        place_camera_metadata(dst, dst_size, src->entry_count, src->data_count);
+
+    metadata->flags = src->flags;
+    metadata->entry_count = src->entry_count;
+    metadata->data_count = src->data_count;
+    metadata->vendor_id = src->vendor_id;
+
+    memcpy(get_entries(metadata), get_entries(src),
+            sizeof(camera_metadata_buffer_entry_t[metadata->entry_count]));
+    memcpy(get_data(metadata), get_data(src),
+            sizeof(uint8_t[metadata->data_count]));
+
+    assert(validate_camera_metadata_structure(metadata, NULL) == OK);
+    return metadata;
+}
+
+// This method should be used when the camera metadata cannot be trusted. For example, when it's
+// read from Parcel.
+static int validate_and_calculate_camera_metadata_entry_data_size(size_t *data_size, uint8_t type,
+        size_t data_count) {
+    if (type >= NUM_TYPES) return ERROR;
+
+    // Check for overflow
+    if (data_count != 0 &&
+            camera_metadata_type_size[type] > (SIZE_MAX - DATA_ALIGNMENT + 1) / data_count) {
+        // android_errorWriteLog(SN_EVENT_LOG_ID, "30741779");
+        return ERROR;
+    }
+
+    size_t data_bytes = data_count * camera_metadata_type_size[type];
+
+    if (data_size) {
+        *data_size = data_bytes <= 4 ? 0 : ALIGN_TO(data_bytes, DATA_ALIGNMENT);
+    }
+
+    return OK;
+}
+
+size_t calculate_camera_metadata_entry_data_size(uint8_t type,
+        size_t data_count) {
+    if (type >= NUM_TYPES) return 0;
+
+    size_t data_bytes = data_count *
+            camera_metadata_type_size[type];
+
+    return data_bytes <= 4 ? 0 : ALIGN_TO(data_bytes, DATA_ALIGNMENT);
+}
+
+int validate_camera_metadata_structure(const camera_metadata_t *metadata,
+                                       const size_t *expected_size) {
+
+    if (metadata == NULL) {
+        ALOGE("%s: metadata is null!", __FUNCTION__);
+        return CAMERA_METADATA_VALIDATION_ERROR;
+    }
+
+    uintptr_t aligned_ptr = ALIGN_TO(metadata, METADATA_PACKET_ALIGNMENT);
+    const uintptr_t alignmentOffset = aligned_ptr - (uintptr_t) metadata;
+
+    // Check that the metadata pointer is well-aligned first.
+    {
+        static const struct {
+            const char *name;
+            size_t alignment;
+        } alignments[] = {
+            {
+                .name = "camera_metadata",
+                .alignment = METADATA_ALIGNMENT
+            },
+            {
+                .name = "camera_metadata_buffer_entry",
+                .alignment = ENTRY_ALIGNMENT
+            },
+            {
+                .name = "camera_metadata_data",
+                .alignment = DATA_ALIGNMENT
+            },
+        };
+
+        for (size_t i = 0; i < sizeof(alignments)/sizeof(alignments[0]); ++i) {
+            uintptr_t aligned_ptr = ALIGN_TO((uintptr_t) metadata + alignmentOffset,
+                    alignments[i].alignment);
+
+            if ((uintptr_t)metadata + alignmentOffset != aligned_ptr) {
+                ALOGE("%s: Metadata pointer is not aligned (actual %p, "
+                      "expected %p, offset %" PRIuPTR ") to type %s",
+                      __FUNCTION__, metadata,
+                      (void*)aligned_ptr, alignmentOffset, alignments[i].name);
+                return CAMERA_METADATA_VALIDATION_ERROR;
+            }
+        }
+    }
+
+    /**
+     * Check that the metadata contents are correct
+     */
+
+    if (expected_size != NULL && metadata->size > *expected_size) {
+        ALOGE("%s: Metadata size (%" PRIu32 ") should be <= expected size (%zu)",
+              __FUNCTION__, metadata->size, *expected_size);
+        return CAMERA_METADATA_VALIDATION_ERROR;
+    }
+
+    if (metadata->entry_count > metadata->entry_capacity) {
+        ALOGE("%s: Entry count (%" PRIu32 ") should be <= entry capacity "
+              "(%" PRIu32 ")",
+              __FUNCTION__, metadata->entry_count, metadata->entry_capacity);
+        return CAMERA_METADATA_VALIDATION_ERROR;
+    }
+
+    if (metadata->data_count > metadata->data_capacity) {
+        ALOGE("%s: Data count (%" PRIu32 ") should be <= data capacity "
+              "(%" PRIu32 ")",
+              __FUNCTION__, metadata->data_count, metadata->data_capacity);
+        // android_errorWriteLog(SN_EVENT_LOG_ID, "30591838");
+        return CAMERA_METADATA_VALIDATION_ERROR;
+    }
+
+    const metadata_uptrdiff_t entries_end =
+        metadata->entries_start + metadata->entry_capacity;
+    if (entries_end < metadata->entries_start || // overflow check
+        entries_end > metadata->data_start) {
+
+        ALOGE("%s: Entry start + capacity (%" PRIu32 ") should be <= data start "
+              "(%" PRIu32 ")",
+               __FUNCTION__,
+              (metadata->entries_start + metadata->entry_capacity),
+              metadata->data_start);
+        return CAMERA_METADATA_VALIDATION_ERROR;
+    }
+
+    const metadata_uptrdiff_t data_end =
+        metadata->data_start + metadata->data_capacity;
+    if (data_end < metadata->data_start || // overflow check
+        data_end > metadata->size) {
+
+        ALOGE("%s: Data start + capacity (%" PRIu32 ") should be <= total size "
+              "(%" PRIu32 ")",
+               __FUNCTION__,
+              (metadata->data_start + metadata->data_capacity),
+              metadata->size);
+        return CAMERA_METADATA_VALIDATION_ERROR;
+    }
+
+    // Validate each entry
+    const metadata_size_t entry_count = metadata->entry_count;
+    camera_metadata_buffer_entry_t *entries = get_entries(metadata);
+
+    for (size_t i = 0; i < entry_count; ++i) {
+
+        if ((uintptr_t)&entries[i] + alignmentOffset !=
+                ALIGN_TO((uintptr_t)&entries[i] + alignmentOffset, ENTRY_ALIGNMENT)) {
+            ALOGE("%s: Entry index %zu had bad alignment (address %p),"
+                  " expected alignment %zu",
+                  __FUNCTION__, i, &entries[i], ENTRY_ALIGNMENT);
+            return CAMERA_METADATA_VALIDATION_ERROR;
+        }
+
+        camera_metadata_buffer_entry_t entry = entries[i];
+
+        if (entry.type >= NUM_TYPES) {
+            ALOGE("%s: Entry index %zu had a bad type %d",
+                  __FUNCTION__, i, entry.type);
+            return CAMERA_METADATA_VALIDATION_ERROR;
+        }
+
+        // TODO: fix vendor_tag_ops across processes so we don't need to special
+        //       case vendor-specific tags
+        uint32_t tag_section = entry.tag >> 16;
+        int tag_type = get_local_camera_metadata_tag_type(entry.tag, metadata);
+        if (tag_type != (int)entry.type && tag_section < VENDOR_SECTION) {
+            ALOGE("%s: Entry index %zu had tag type %d, but the type was %d",
+                  __FUNCTION__, i, tag_type, entry.type);
+            return CAMERA_METADATA_VALIDATION_ERROR;
+        }
+
+        size_t data_size;
+        if (validate_and_calculate_camera_metadata_entry_data_size(&data_size, entry.type,
+                entry.count) != OK) {
+            ALOGE("%s: Entry data size is invalid. type: %u count: %u", __FUNCTION__, entry.type,
+                    entry.count);
+            return CAMERA_METADATA_VALIDATION_ERROR;
+        }
+
+        if (data_size != 0) {
+            camera_metadata_data_t *data =
+                    (camera_metadata_data_t*) (get_data(metadata) +
+                                               entry.data.offset);
+
+            if ((uintptr_t)data + alignmentOffset !=
+                        ALIGN_TO((uintptr_t)data + alignmentOffset, DATA_ALIGNMENT)) {
+                ALOGE("%s: Entry index %zu had bad data alignment (address %p),"
+                      " expected align %zu, (tag name %s, data size %zu)",
+                      __FUNCTION__, i, data, DATA_ALIGNMENT,
+                      get_local_camera_metadata_tag_name(entry.tag, metadata) ?
+                              : "unknown", data_size);
+                return CAMERA_METADATA_VALIDATION_ERROR;
+            }
+
+            size_t data_entry_end = entry.data.offset + data_size;
+            if (data_entry_end < entry.data.offset || // overflow check
+                data_entry_end > metadata->data_capacity) {
+
+                ALOGE("%s: Entry index %zu data ends (%zu) beyond the capacity "
+                      "%" PRIu32, __FUNCTION__, i, data_entry_end,
+                      metadata->data_capacity);
+                return CAMERA_METADATA_VALIDATION_ERROR;
+            }
+
+        } else if (entry.count == 0) {
+            if (entry.data.offset != 0) {
+                ALOGE("%s: Entry index %zu had 0 items, but offset was non-0 "
+                     "(%" PRIu32 "), tag name: %s", __FUNCTION__, i, entry.data.offset,
+                        get_local_camera_metadata_tag_name(entry.tag, metadata) ? : "unknown");
+                return CAMERA_METADATA_VALIDATION_ERROR;
+            }
+        } // else data stored inline, so we look at value which can be anything.
+    }
+
+    if (alignmentOffset == 0) {
+        return OK;
+    }
+    return CAMERA_METADATA_VALIDATION_SHIFTED;
+}
+
+int append_camera_metadata(camera_metadata_t *dst,
+        const camera_metadata_t *src) {
+    if (dst == NULL || src == NULL ) return ERROR;
+
+    // Check for overflow
+    if (src->entry_count + dst->entry_count < src->entry_count) return ERROR;
+    if (src->data_count + dst->data_count < src->data_count) return ERROR;
+    // Check for space
+    if (dst->entry_capacity < src->entry_count + dst->entry_count) return ERROR;
+    if (dst->data_capacity < src->data_count + dst->data_count) return ERROR;
+
+    if ((dst->vendor_id != CAMERA_METADATA_INVALID_VENDOR_ID) &&
+            (src->vendor_id != CAMERA_METADATA_INVALID_VENDOR_ID)) {
+        if (dst->vendor_id != src->vendor_id) {
+            ALOGE("%s: Append for metadata from different vendors is"
+                    "not supported!", __func__);
+            return ERROR;
+        }
+    }
+
+    memcpy(get_entries(dst) + dst->entry_count, get_entries(src),
+            sizeof(camera_metadata_buffer_entry_t[src->entry_count]));
+    memcpy(get_data(dst) + dst->data_count, get_data(src),
+            sizeof(uint8_t[src->data_count]));
+    if (dst->data_count != 0) {
+        camera_metadata_buffer_entry_t *entry = get_entries(dst) + dst->entry_count;
+        for (size_t i = 0; i < src->entry_count; i++, entry++) {
+            if ( calculate_camera_metadata_entry_data_size(entry->type,
+                            entry->count) > 0 ) {
+                entry->data.offset += dst->data_count;
+            }
+        }
+    }
+    if (dst->entry_count == 0) {
+        // Appending onto empty buffer, keep sorted state
+        dst->flags |= src->flags & FLAG_SORTED;
+    } else if (src->entry_count != 0) {
+        // Both src, dst are nonempty, cannot assume sort remains
+        dst->flags &= ~FLAG_SORTED;
+    } else {
+        // Src is empty, keep dst sorted state
+    }
+    dst->entry_count += src->entry_count;
+    dst->data_count += src->data_count;
+
+    if (dst->vendor_id == CAMERA_METADATA_INVALID_VENDOR_ID) {
+        dst->vendor_id = src->vendor_id;
+    }
+
+    assert(validate_camera_metadata_structure(dst, NULL) == OK);
+    return OK;
+}
+
+camera_metadata_t *clone_camera_metadata(const camera_metadata_t *src) {
+    int res;
+    if (src == NULL) return NULL;
+    camera_metadata_t *clone = allocate_camera_metadata(
+        get_camera_metadata_entry_count(src),
+        get_camera_metadata_data_count(src));
+    if (clone != NULL) {
+        res = append_camera_metadata(clone, src);
+        if (res != OK) {
+            free_camera_metadata(clone);
+            clone = NULL;
+        }
+    }
+    assert(validate_camera_metadata_structure(clone, NULL) == OK);
+    return clone;
+}
+
+static int add_camera_metadata_entry_raw(camera_metadata_t *dst,
+        uint32_t tag,
+        uint8_t  type,
+        const void *data,
+        size_t data_count) {
+
+    if (dst == NULL) return ERROR;
+    if (dst->entry_count == dst->entry_capacity) return ERROR;
+    if (data_count && data == NULL) return ERROR;
+
+    size_t data_bytes =
+            calculate_camera_metadata_entry_data_size(type, data_count);
+    if (data_bytes + dst->data_count > dst->data_capacity) return ERROR;
+
+    size_t data_payload_bytes =
+            data_count * camera_metadata_type_size[type];
+    camera_metadata_buffer_entry_t *entry = get_entries(dst) + dst->entry_count;
+    memset(entry, 0, sizeof(camera_metadata_buffer_entry_t));
+    entry->tag = tag;
+    entry->type = type;
+    entry->count = data_count;
+
+    if (data_bytes == 0) {
+        memcpy(entry->data.value, data,
+                data_payload_bytes);
+    } else {
+        entry->data.offset = dst->data_count;
+        memcpy(get_data(dst) + entry->data.offset, data,
+                data_payload_bytes);
+        dst->data_count += data_bytes;
+    }
+    dst->entry_count++;
+    dst->flags &= ~FLAG_SORTED;
+    assert(validate_camera_metadata_structure(dst, NULL) == OK);
+    return OK;
+}
+
+int add_camera_metadata_entry(camera_metadata_t *dst,
+        uint32_t tag,
+        const void *data,
+        size_t data_count) {
+
+    int type = get_local_camera_metadata_tag_type(tag, dst);
+    if (type == -1) {
+        ALOGE("%s: Unknown tag %04x.", __FUNCTION__, tag);
+        return ERROR;
+    }
+
+    return add_camera_metadata_entry_raw(dst,
+            tag,
+            type,
+            data,
+            data_count);
+}
+
+static int compare_entry_tags(const void *p1, const void *p2) {
+    uint32_t tag1 = ((camera_metadata_buffer_entry_t*)p1)->tag;
+    uint32_t tag2 = ((camera_metadata_buffer_entry_t*)p2)->tag;
+    return  tag1 < tag2 ? -1 :
+            tag1 == tag2 ? 0 :
+            1;
+}
+
+int sort_camera_metadata(camera_metadata_t *dst) {
+    if (dst == NULL) return ERROR;
+    if (dst->flags & FLAG_SORTED) return OK;
+
+    qsort(get_entries(dst), dst->entry_count,
+            sizeof(camera_metadata_buffer_entry_t),
+            compare_entry_tags);
+    dst->flags |= FLAG_SORTED;
+
+    assert(validate_camera_metadata_structure(dst, NULL) == OK);
+    return OK;
+}
+
+int get_camera_metadata_entry(camera_metadata_t *src,
+        size_t index,
+        camera_metadata_entry_t *entry) {
+    if (src == NULL || entry == NULL) return ERROR;
+    if (index >= src->entry_count) return ERROR;
+
+    camera_metadata_buffer_entry_t *buffer_entry = get_entries(src) + index;
+
+    entry->index = index;
+    entry->tag = buffer_entry->tag;
+    entry->type = buffer_entry->type;
+    entry->count = buffer_entry->count;
+    if (buffer_entry->count *
+            camera_metadata_type_size[buffer_entry->type] > 4) {
+        entry->data.u8 = get_data(src) + buffer_entry->data.offset;
+    } else {
+        entry->data.u8 = buffer_entry->data.value;
+    }
+    return OK;
+}
+
+int get_camera_metadata_ro_entry(const camera_metadata_t *src,
+        size_t index,
+        camera_metadata_ro_entry_t *entry) {
+    return get_camera_metadata_entry((camera_metadata_t*)src, index,
+            (camera_metadata_entry_t*)entry);
+}
+
+int find_camera_metadata_entry(camera_metadata_t *src,
+        uint32_t tag,
+        camera_metadata_entry_t *entry) {
+    if (src == NULL) return ERROR;
+
+    uint32_t index;
+    if (src->flags & FLAG_SORTED) {
+        // Sorted entries, do a binary search
+        camera_metadata_buffer_entry_t *search_entry = NULL;
+        camera_metadata_buffer_entry_t key;
+        key.tag = tag;
+        search_entry = bsearch(&key,
+                get_entries(src),
+                src->entry_count,
+                sizeof(camera_metadata_buffer_entry_t),
+                compare_entry_tags);
+        if (search_entry == NULL) return NOT_FOUND;
+        index = search_entry - get_entries(src);
+    } else {
+        // Not sorted, linear search
+        camera_metadata_buffer_entry_t *search_entry = get_entries(src);
+        for (index = 0; index < src->entry_count; index++, search_entry++) {
+            if (search_entry->tag == tag) {
+                break;
+            }
+        }
+        if (index == src->entry_count) return NOT_FOUND;
+    }
+
+    return get_camera_metadata_entry(src, index,
+            entry);
+}
+
+int find_camera_metadata_ro_entry(const camera_metadata_t *src,
+        uint32_t tag,
+        camera_metadata_ro_entry_t *entry) {
+    return find_camera_metadata_entry((camera_metadata_t*)src, tag,
+            (camera_metadata_entry_t*)entry);
+}
+
+
+int delete_camera_metadata_entry(camera_metadata_t *dst,
+        size_t index) {
+    if (dst == NULL) return ERROR;
+    if (index >= dst->entry_count) return ERROR;
+
+    camera_metadata_buffer_entry_t *entry = get_entries(dst) + index;
+    size_t data_bytes = calculate_camera_metadata_entry_data_size(entry->type,
+            entry->count);
+
+    if (data_bytes > 0) {
+        // Shift data buffer to overwrite deleted data
+        uint8_t *start = get_data(dst) + entry->data.offset;
+        uint8_t *end = start + data_bytes;
+        size_t length = dst->data_count - entry->data.offset - data_bytes;
+        memmove(start, end, length);
+
+        // Update all entry indices to account for shift
+        camera_metadata_buffer_entry_t *e = get_entries(dst);
+        size_t i;
+        for (i = 0; i < dst->entry_count; i++) {
+            if (calculate_camera_metadata_entry_data_size(
+                    e->type, e->count) > 0 &&
+                    e->data.offset > entry->data.offset) {
+                e->data.offset -= data_bytes;
+            }
+            ++e;
+        }
+        dst->data_count -= data_bytes;
+    }
+    // Shift entry array
+    memmove(entry, entry + 1,
+            sizeof(camera_metadata_buffer_entry_t) *
+            (dst->entry_count - index - 1) );
+    dst->entry_count -= 1;
+
+    assert(validate_camera_metadata_structure(dst, NULL) == OK);
+    return OK;
+}
+
+int update_camera_metadata_entry(camera_metadata_t *dst,
+        size_t index,
+        const void *data,
+        size_t data_count,
+        camera_metadata_entry_t *updated_entry) {
+    if (dst == NULL) return ERROR;
+    if (index >= dst->entry_count) return ERROR;
+
+    camera_metadata_buffer_entry_t *entry = get_entries(dst) + index;
+
+    size_t data_bytes =
+            calculate_camera_metadata_entry_data_size(entry->type,
+                    data_count);
+    size_t data_payload_bytes =
+            data_count * camera_metadata_type_size[entry->type];
+
+    size_t entry_bytes =
+            calculate_camera_metadata_entry_data_size(entry->type,
+                    entry->count);
+    if (data_bytes != entry_bytes) {
+        // May need to shift/add to data array
+        if (dst->data_capacity < dst->data_count + data_bytes - entry_bytes) {
+            // No room
+            return ERROR;
+        }
+        if (entry_bytes != 0) {
+            // Remove old data
+            uint8_t *start = get_data(dst) + entry->data.offset;
+            uint8_t *end = start + entry_bytes;
+            size_t length = dst->data_count - entry->data.offset - entry_bytes;
+            memmove(start, end, length);
+            dst->data_count -= entry_bytes;
+
+            // Update all entry indices to account for shift
+            camera_metadata_buffer_entry_t *e = get_entries(dst);
+            size_t i;
+            for (i = 0; i < dst->entry_count; i++) {
+                if (calculate_camera_metadata_entry_data_size(
+                        e->type, e->count) > 0 &&
+                        e->data.offset > entry->data.offset) {
+                    e->data.offset -= entry_bytes;
+                }
+                ++e;
+            }
+        }
+
+        if (data_bytes != 0) {
+            // Append new data
+            entry->data.offset = dst->data_count;
+
+            memcpy(get_data(dst) + entry->data.offset, data, data_payload_bytes);
+            dst->data_count += data_bytes;
+        }
+    } else if (data_bytes != 0) {
+        // data size unchanged, reuse same data location
+        memcpy(get_data(dst) + entry->data.offset, data, data_payload_bytes);
+    }
+
+    if (data_bytes == 0) {
+        // Data fits into entry
+        memcpy(entry->data.value, data,
+                data_payload_bytes);
+    }
+
+    entry->count = data_count;
+
+    if (updated_entry != NULL) {
+        get_camera_metadata_entry(dst,
+                index,
+                updated_entry);
+    }
+
+    assert(validate_camera_metadata_structure(dst, NULL) == OK);
+    return OK;
+}
+
+static const vendor_tag_ops_t *vendor_tag_ops = NULL;
+static const struct vendor_tag_cache_ops *vendor_cache_ops = NULL;
+
+// Declared in system/media/private/camera/include/camera_metadata_hidden.h
+const char *get_local_camera_metadata_section_name_vendor_id(uint32_t tag,
+        metadata_vendor_id_t id) {
+    uint32_t tag_section = tag >> 16;
+    if (tag_section >= VENDOR_SECTION && vendor_cache_ops != NULL &&
+               id != CAMERA_METADATA_INVALID_VENDOR_ID) {
+           return vendor_cache_ops->get_section_name(tag, id);
+    } else if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
+        return vendor_tag_ops->get_section_name(
+            vendor_tag_ops,
+            tag);
+    }
+    if (tag_section >= ANDROID_SECTION_COUNT) {
+        return NULL;
+    }
+    return camera_metadata_section_names[tag_section];
+}
+
+// Declared in system/media/private/camera/include/camera_metadata_hidden.h
+const char *get_local_camera_metadata_tag_name_vendor_id(uint32_t tag,
+        metadata_vendor_id_t id) {
+    uint32_t tag_section = tag >> 16;
+    if (tag_section >= VENDOR_SECTION && vendor_cache_ops != NULL &&
+                id != CAMERA_METADATA_INVALID_VENDOR_ID) {
+            return vendor_cache_ops->get_tag_name(tag, id);
+    } else  if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
+        return vendor_tag_ops->get_tag_name(
+            vendor_tag_ops,
+            tag);
+    }
+    if (tag_section >= ANDROID_SECTION_COUNT ||
+        tag >= camera_metadata_section_bounds[tag_section][1] ) {
+        return NULL;
+    }
+    uint32_t tag_index = tag & 0xFFFF;
+    return tag_info[tag_section][tag_index].tag_name;
+}
+
+// Declared in system/media/private/camera/include/camera_metadata_hidden.h
+int get_local_camera_metadata_tag_type_vendor_id(uint32_t tag,
+        metadata_vendor_id_t id) {
+    uint32_t tag_section = tag >> 16;
+    if (tag_section >= VENDOR_SECTION && vendor_cache_ops != NULL &&
+                id != CAMERA_METADATA_INVALID_VENDOR_ID) {
+            return vendor_cache_ops->get_tag_type(tag, id);
+    } else if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
+        return vendor_tag_ops->get_tag_type(
+            vendor_tag_ops,
+            tag);
+    }
+    if (tag_section >= ANDROID_SECTION_COUNT ||
+            tag >= camera_metadata_section_bounds[tag_section][1] ) {
+        return -1;
+    }
+    uint32_t tag_index = tag & 0xFFFF;
+    return tag_info[tag_section][tag_index].tag_type;
+}
+
+const char *get_camera_metadata_section_name(uint32_t tag) {
+    return get_local_camera_metadata_section_name(tag, NULL);
+}
+
+const char *get_camera_metadata_tag_name(uint32_t tag) {
+    return get_local_camera_metadata_tag_name(tag, NULL);
+}
+
+int get_camera_metadata_tag_type(uint32_t tag) {
+    return get_local_camera_metadata_tag_type(tag, NULL);
+}
+
+const char *get_local_camera_metadata_section_name(uint32_t tag,
+        const camera_metadata_t *meta) {
+    metadata_vendor_id_t id = (NULL == meta) ? CAMERA_METADATA_INVALID_VENDOR_ID :
+            meta->vendor_id;
+
+    return get_local_camera_metadata_section_name_vendor_id(tag, id);
+}
+
+const char *get_local_camera_metadata_tag_name(uint32_t tag,
+        const camera_metadata_t *meta) {
+    metadata_vendor_id_t id = (NULL == meta) ? CAMERA_METADATA_INVALID_VENDOR_ID :
+            meta->vendor_id;
+
+    return get_local_camera_metadata_tag_name_vendor_id(tag, id);
+}
+
+int get_local_camera_metadata_tag_type(uint32_t tag,
+        const camera_metadata_t *meta) {
+    metadata_vendor_id_t id = (NULL == meta) ? CAMERA_METADATA_INVALID_VENDOR_ID :
+            meta->vendor_id;
+
+    return get_local_camera_metadata_tag_type_vendor_id(tag, id);
+}
+
+int set_camera_metadata_vendor_tag_ops(const vendor_tag_query_ops_t* ops) {
+    // **DEPRECATED**
+    (void) ops;
+    ALOGE("%s: This function has been deprecated", __FUNCTION__);
+    return ERROR;
+}
+
+// Declared in system/media/private/camera/include/camera_metadata_hidden.h
+int set_camera_metadata_vendor_ops(const vendor_tag_ops_t* ops) {
+    vendor_tag_ops = ops;
+    return OK;
+}
+
+// Declared in system/media/private/camera/include/camera_metadata_hidden.h
+int set_camera_metadata_vendor_cache_ops(
+        const struct vendor_tag_cache_ops *query_cache_ops) {
+    vendor_cache_ops = query_cache_ops;
+    return OK;
+}
+
+// Declared in system/media/private/camera/include/camera_metadata_hidden.h
+void set_camera_metadata_vendor_id(camera_metadata_t *meta,
+        metadata_vendor_id_t id) {
+    if (NULL != meta) {
+        meta->vendor_id = id;
+    }
+}
+
+// Declared in system/media/private/camera/include/camera_metadata_hidden.h
+metadata_vendor_id_t get_camera_metadata_vendor_id(
+        const camera_metadata_t *meta) {
+    metadata_vendor_id_t ret = CAMERA_METADATA_INVALID_VENDOR_ID;
+
+    if (NULL != meta) {
+        ret = meta->vendor_id;
+    }
+
+    return ret;
+}
+
+static void print_data(int fd, const uint8_t *data_ptr, uint32_t tag, int type,
+        int count,
+        int indentation);
+
+void dump_camera_metadata(const camera_metadata_t *metadata,
+        int fd,
+        int verbosity) {
+    dump_indented_camera_metadata(metadata, fd, verbosity, 0);
+}
+
+void dump_indented_camera_metadata(const camera_metadata_t *metadata,
+        int fd,
+        int verbosity,
+        int indentation) {
+    if (metadata == NULL) {
+        dprintf(fd, "%*sDumping camera metadata array: Not allocated\n",
+                indentation, "");
+        return;
+    }
+    unsigned int i;
+    dprintf(fd,
+            "%*sDumping camera metadata array: %" PRIu32 " / %" PRIu32 " entries, "
+            "%" PRIu32 " / %" PRIu32 " bytes of extra data.\n", indentation, "",
+            metadata->entry_count, metadata->entry_capacity,
+            metadata->data_count, metadata->data_capacity);
+    dprintf(fd, "%*sVersion: %d, Flags: %08x\n",
+            indentation + 2, "",
+            metadata->version, metadata->flags);
+    camera_metadata_buffer_entry_t *entry = get_entries(metadata);
+    for (i=0; i < metadata->entry_count; i++, entry++) {
+
+        const char *tag_name, *tag_section;
+        tag_section = get_local_camera_metadata_section_name(entry->tag, metadata);
+        if (tag_section == NULL) {
+            tag_section = "unknownSection";
+        }
+        tag_name = get_local_camera_metadata_tag_name(entry->tag, metadata);
+        if (tag_name == NULL) {
+            tag_name = "unknownTag";
+        }
+        const char *type_name;
+        if (entry->type >= NUM_TYPES) {
+            type_name = "unknown";
+        } else {
+            type_name = camera_metadata_type_names[entry->type];
+        }
+        dprintf(fd, "%*s%s.%s (%05x): %s[%" PRIu32 "]\n",
+             indentation + 2, "",
+             tag_section,
+             tag_name,
+             entry->tag,
+             type_name,
+             entry->count);
+
+        if (verbosity < 1) continue;
+
+        if (entry->type >= NUM_TYPES) continue;
+
+        size_t type_size = camera_metadata_type_size[entry->type];
+        uint8_t *data_ptr;
+        if ( type_size * entry->count > 4 ) {
+            if (entry->data.offset >= metadata->data_count) {
+                ALOGE("%s: Malformed entry data offset: %" PRIu32 " (max %" PRIu32 ")",
+                        __FUNCTION__,
+                        entry->data.offset,
+                        metadata->data_count);
+                continue;
+            }
+            data_ptr = get_data(metadata) + entry->data.offset;
+        } else {
+            data_ptr = entry->data.value;
+        }
+        int count = entry->count;
+        if (verbosity < 2 && count > 16) count = 16;
+
+        print_data(fd, data_ptr, entry->tag, entry->type, count, indentation);
+    }
+}
+
+static void print_data(int fd, const uint8_t *data_ptr, uint32_t tag,
+        int type, int count, int indentation) {
+    static int values_per_line[NUM_TYPES] = {
+        [TYPE_BYTE]     = 16,
+        [TYPE_INT32]    = 4,
+        [TYPE_FLOAT]    = 8,
+        [TYPE_INT64]    = 2,
+        [TYPE_DOUBLE]   = 4,
+        [TYPE_RATIONAL] = 2,
+    };
+    size_t type_size = camera_metadata_type_size[type];
+    char value_string_tmp[CAMERA_METADATA_ENUM_STRING_MAX_SIZE];
+    uint32_t value;
+
+    int lines = count / values_per_line[type];
+    if (count % values_per_line[type] != 0) lines++;
+
+    int index = 0;
+    int j, k;
+    for (j = 0; j < lines; j++) {
+        dprintf(fd, "%*s[", indentation + 4, "");
+        for (k = 0;
+             k < values_per_line[type] && count > 0;
+             k++, count--, index += type_size) {
+
+            switch (type) {
+                case TYPE_BYTE:
+                    value = *(data_ptr + index);
+                    if (camera_metadata_enum_snprint(tag,
+                                                     value,
+                                                     value_string_tmp,
+                                                     sizeof(value_string_tmp))
+                        == OK) {
+                        dprintf(fd, "%s ", value_string_tmp);
+                    } else {
+                        dprintf(fd, "%hhu ",
+                                *(data_ptr + index));
+                    }
+                    break;
+                case TYPE_INT32:
+                    value =
+                            *(int32_t*)(data_ptr + index);
+                    if (camera_metadata_enum_snprint(tag,
+                                                     value,
+                                                     value_string_tmp,
+                                                     sizeof(value_string_tmp))
+                        == OK) {
+                        dprintf(fd, "%s ", value_string_tmp);
+                    } else {
+                        dprintf(fd, "%" PRId32 " ",
+                                *(int32_t*)(data_ptr + index));
+                    }
+                    break;
+                case TYPE_FLOAT:
+                    dprintf(fd, "%0.8f ",
+                            *(float*)(data_ptr + index));
+                    break;
+                case TYPE_INT64:
+                    dprintf(fd, "%" PRId64 " ",
+                            *(int64_t*)(data_ptr + index));
+                    break;
+                case TYPE_DOUBLE:
+                    dprintf(fd, "%0.8f ",
+                            *(double*)(data_ptr + index));
+                    break;
+                case TYPE_RATIONAL: {
+                    int32_t numerator = *(int32_t*)(data_ptr + index);
+                    int32_t denominator = *(int32_t*)(data_ptr + index + 4);
+                    dprintf(fd, "(%d / %d) ",
+                            numerator, denominator);
+                    break;
+                }
+                default:
+                    dprintf(fd, "??? ");
+            }
+        }
+        dprintf(fd, "]\n");
+    }
+}
diff --git a/src/android/metadata/camera_metadata_tag_info.c b/src/android/metadata/camera_metadata_tag_info.c
new file mode 100644
index 000000000000..75ad1f4ca244
--- /dev/null
+++ b/src/android/metadata/camera_metadata_tag_info.c
@@ -0,0 +1,2811 @@ 
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/**
+ * !! Do not reference this file directly !!
+ *
+ * It is logically a part of camera_metadata.c.  It is broken out for ease of
+ * maintaining the tag info.
+ *
+ * Array assignments are done using specified-index syntax to keep things in
+ * sync with camera_metadata_tags.h
+ */
+
+/**
+ * ! Do not edit this file directly !
+ *
+ * Generated automatically from camera_metadata_tag_info.mako
+ */
+
+const char *camera_metadata_section_names[ANDROID_SECTION_COUNT] = {
+    [ANDROID_COLOR_CORRECTION]     = "android.colorCorrection",
+    [ANDROID_CONTROL]              = "android.control",
+    [ANDROID_DEMOSAIC]             = "android.demosaic",
+    [ANDROID_EDGE]                 = "android.edge",
+    [ANDROID_FLASH]                = "android.flash",
+    [ANDROID_FLASH_INFO]           = "android.flash.info",
+    [ANDROID_HOT_PIXEL]            = "android.hotPixel",
+    [ANDROID_JPEG]                 = "android.jpeg",
+    [ANDROID_LENS]                 = "android.lens",
+    [ANDROID_LENS_INFO]            = "android.lens.info",
+    [ANDROID_NOISE_REDUCTION]      = "android.noiseReduction",
+    [ANDROID_QUIRKS]               = "android.quirks",
+    [ANDROID_REQUEST]              = "android.request",
+    [ANDROID_SCALER]               = "android.scaler",
+    [ANDROID_SENSOR]               = "android.sensor",
+    [ANDROID_SENSOR_INFO]          = "android.sensor.info",
+    [ANDROID_SHADING]              = "android.shading",
+    [ANDROID_STATISTICS]           = "android.statistics",
+    [ANDROID_STATISTICS_INFO]      = "android.statistics.info",
+    [ANDROID_TONEMAP]              = "android.tonemap",
+    [ANDROID_LED]                  = "android.led",
+    [ANDROID_INFO]                 = "android.info",
+    [ANDROID_BLACK_LEVEL]          = "android.blackLevel",
+    [ANDROID_SYNC]                 = "android.sync",
+    [ANDROID_REPROCESS]            = "android.reprocess",
+    [ANDROID_DEPTH]                = "android.depth",
+    [ANDROID_LOGICAL_MULTI_CAMERA] = "android.logicalMultiCamera",
+    [ANDROID_DISTORTION_CORRECTION]
+                                    = "android.distortionCorrection",
+};
+
+unsigned int camera_metadata_section_bounds[ANDROID_SECTION_COUNT][2] = {
+    [ANDROID_COLOR_CORRECTION]     = { ANDROID_COLOR_CORRECTION_START,
+                                       ANDROID_COLOR_CORRECTION_END },
+    [ANDROID_CONTROL]              = { ANDROID_CONTROL_START,
+                                       ANDROID_CONTROL_END },
+    [ANDROID_DEMOSAIC]             = { ANDROID_DEMOSAIC_START,
+                                       ANDROID_DEMOSAIC_END },
+    [ANDROID_EDGE]                 = { ANDROID_EDGE_START,
+                                       ANDROID_EDGE_END },
+    [ANDROID_FLASH]                = { ANDROID_FLASH_START,
+                                       ANDROID_FLASH_END },
+    [ANDROID_FLASH_INFO]           = { ANDROID_FLASH_INFO_START,
+                                       ANDROID_FLASH_INFO_END },
+    [ANDROID_HOT_PIXEL]            = { ANDROID_HOT_PIXEL_START,
+                                       ANDROID_HOT_PIXEL_END },
+    [ANDROID_JPEG]                 = { ANDROID_JPEG_START,
+                                       ANDROID_JPEG_END },
+    [ANDROID_LENS]                 = { ANDROID_LENS_START,
+                                       ANDROID_LENS_END },
+    [ANDROID_LENS_INFO]            = { ANDROID_LENS_INFO_START,
+                                       ANDROID_LENS_INFO_END },
+    [ANDROID_NOISE_REDUCTION]      = { ANDROID_NOISE_REDUCTION_START,
+                                       ANDROID_NOISE_REDUCTION_END },
+    [ANDROID_QUIRKS]               = { ANDROID_QUIRKS_START,
+                                       ANDROID_QUIRKS_END },
+    [ANDROID_REQUEST]              = { ANDROID_REQUEST_START,
+                                       ANDROID_REQUEST_END },
+    [ANDROID_SCALER]               = { ANDROID_SCALER_START,
+                                       ANDROID_SCALER_END },
+    [ANDROID_SENSOR]               = { ANDROID_SENSOR_START,
+                                       ANDROID_SENSOR_END },
+    [ANDROID_SENSOR_INFO]          = { ANDROID_SENSOR_INFO_START,
+                                       ANDROID_SENSOR_INFO_END },
+    [ANDROID_SHADING]              = { ANDROID_SHADING_START,
+                                       ANDROID_SHADING_END },
+    [ANDROID_STATISTICS]           = { ANDROID_STATISTICS_START,
+                                       ANDROID_STATISTICS_END },
+    [ANDROID_STATISTICS_INFO]      = { ANDROID_STATISTICS_INFO_START,
+                                       ANDROID_STATISTICS_INFO_END },
+    [ANDROID_TONEMAP]              = { ANDROID_TONEMAP_START,
+                                       ANDROID_TONEMAP_END },
+    [ANDROID_LED]                  = { ANDROID_LED_START,
+                                       ANDROID_LED_END },
+    [ANDROID_INFO]                 = { ANDROID_INFO_START,
+                                       ANDROID_INFO_END },
+    [ANDROID_BLACK_LEVEL]          = { ANDROID_BLACK_LEVEL_START,
+                                       ANDROID_BLACK_LEVEL_END },
+    [ANDROID_SYNC]                 = { ANDROID_SYNC_START,
+                                       ANDROID_SYNC_END },
+    [ANDROID_REPROCESS]            = { ANDROID_REPROCESS_START,
+                                       ANDROID_REPROCESS_END },
+    [ANDROID_DEPTH]                = { ANDROID_DEPTH_START,
+                                       ANDROID_DEPTH_END },
+    [ANDROID_LOGICAL_MULTI_CAMERA] = { ANDROID_LOGICAL_MULTI_CAMERA_START,
+                                       ANDROID_LOGICAL_MULTI_CAMERA_END },
+    [ANDROID_DISTORTION_CORRECTION]
+                                    = { ANDROID_DISTORTION_CORRECTION_START,
+                                       ANDROID_DISTORTION_CORRECTION_END },
+};
+
+static tag_info_t android_color_correction[ANDROID_COLOR_CORRECTION_END -
+        ANDROID_COLOR_CORRECTION_START] = {
+    [ ANDROID_COLOR_CORRECTION_MODE - ANDROID_COLOR_CORRECTION_START ] =
+    { "mode",                          TYPE_BYTE   },
+    [ ANDROID_COLOR_CORRECTION_TRANSFORM - ANDROID_COLOR_CORRECTION_START ] =
+    { "transform",                     TYPE_RATIONAL
+                },
+    [ ANDROID_COLOR_CORRECTION_GAINS - ANDROID_COLOR_CORRECTION_START ] =
+    { "gains",                         TYPE_FLOAT  },
+    [ ANDROID_COLOR_CORRECTION_ABERRATION_MODE - ANDROID_COLOR_CORRECTION_START ] =
+    { "aberrationMode",                TYPE_BYTE   },
+    [ ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES - ANDROID_COLOR_CORRECTION_START ] =
+    { "availableAberrationModes",      TYPE_BYTE   },
+};
+
+static tag_info_t android_control[ANDROID_CONTROL_END -
+        ANDROID_CONTROL_START] = {
+    [ ANDROID_CONTROL_AE_ANTIBANDING_MODE - ANDROID_CONTROL_START ] =
+    { "aeAntibandingMode",             TYPE_BYTE   },
+    [ ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION - ANDROID_CONTROL_START ] =
+    { "aeExposureCompensation",        TYPE_INT32  },
+    [ ANDROID_CONTROL_AE_LOCK - ANDROID_CONTROL_START ] =
+    { "aeLock",                        TYPE_BYTE   },
+    [ ANDROID_CONTROL_AE_MODE - ANDROID_CONTROL_START ] =
+    { "aeMode",                        TYPE_BYTE   },
+    [ ANDROID_CONTROL_AE_REGIONS - ANDROID_CONTROL_START ] =
+    { "aeRegions",                     TYPE_INT32  },
+    [ ANDROID_CONTROL_AE_TARGET_FPS_RANGE - ANDROID_CONTROL_START ] =
+    { "aeTargetFpsRange",              TYPE_INT32  },
+    [ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER - ANDROID_CONTROL_START ] =
+    { "aePrecaptureTrigger",           TYPE_BYTE   },
+    [ ANDROID_CONTROL_AF_MODE - ANDROID_CONTROL_START ] =
+    { "afMode",                        TYPE_BYTE   },
+    [ ANDROID_CONTROL_AF_REGIONS - ANDROID_CONTROL_START ] =
+    { "afRegions",                     TYPE_INT32  },
+    [ ANDROID_CONTROL_AF_TRIGGER - ANDROID_CONTROL_START ] =
+    { "afTrigger",                     TYPE_BYTE   },
+    [ ANDROID_CONTROL_AWB_LOCK - ANDROID_CONTROL_START ] =
+    { "awbLock",                       TYPE_BYTE   },
+    [ ANDROID_CONTROL_AWB_MODE - ANDROID_CONTROL_START ] =
+    { "awbMode",                       TYPE_BYTE   },
+    [ ANDROID_CONTROL_AWB_REGIONS - ANDROID_CONTROL_START ] =
+    { "awbRegions",                    TYPE_INT32  },
+    [ ANDROID_CONTROL_CAPTURE_INTENT - ANDROID_CONTROL_START ] =
+    { "captureIntent",                 TYPE_BYTE   },
+    [ ANDROID_CONTROL_EFFECT_MODE - ANDROID_CONTROL_START ] =
+    { "effectMode",                    TYPE_BYTE   },
+    [ ANDROID_CONTROL_MODE - ANDROID_CONTROL_START ] =
+    { "mode",                          TYPE_BYTE   },
+    [ ANDROID_CONTROL_SCENE_MODE - ANDROID_CONTROL_START ] =
+    { "sceneMode",                     TYPE_BYTE   },
+    [ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE - ANDROID_CONTROL_START ] =
+    { "videoStabilizationMode",        TYPE_BYTE   },
+    [ ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES - ANDROID_CONTROL_START ] =
+    { "aeAvailableAntibandingModes",   TYPE_BYTE   },
+    [ ANDROID_CONTROL_AE_AVAILABLE_MODES - ANDROID_CONTROL_START ] =
+    { "aeAvailableModes",              TYPE_BYTE   },
+    [ ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES - ANDROID_CONTROL_START ] =
+    { "aeAvailableTargetFpsRanges",    TYPE_INT32  },
+    [ ANDROID_CONTROL_AE_COMPENSATION_RANGE - ANDROID_CONTROL_START ] =
+    { "aeCompensationRange",           TYPE_INT32  },
+    [ ANDROID_CONTROL_AE_COMPENSATION_STEP - ANDROID_CONTROL_START ] =
+    { "aeCompensationStep",            TYPE_RATIONAL
+                },
+    [ ANDROID_CONTROL_AF_AVAILABLE_MODES - ANDROID_CONTROL_START ] =
+    { "afAvailableModes",              TYPE_BYTE   },
+    [ ANDROID_CONTROL_AVAILABLE_EFFECTS - ANDROID_CONTROL_START ] =
+    { "availableEffects",              TYPE_BYTE   },
+    [ ANDROID_CONTROL_AVAILABLE_SCENE_MODES - ANDROID_CONTROL_START ] =
+    { "availableSceneModes",           TYPE_BYTE   },
+    [ ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES - ANDROID_CONTROL_START ] =
+    { "availableVideoStabilizationModes",
+                                        TYPE_BYTE   },
+    [ ANDROID_CONTROL_AWB_AVAILABLE_MODES - ANDROID_CONTROL_START ] =
+    { "awbAvailableModes",             TYPE_BYTE   },
+    [ ANDROID_CONTROL_MAX_REGIONS - ANDROID_CONTROL_START ] =
+    { "maxRegions",                    TYPE_INT32  },
+    [ ANDROID_CONTROL_SCENE_MODE_OVERRIDES - ANDROID_CONTROL_START ] =
+    { "sceneModeOverrides",            TYPE_BYTE   },
+    [ ANDROID_CONTROL_AE_PRECAPTURE_ID - ANDROID_CONTROL_START ] =
+    { "aePrecaptureId",                TYPE_INT32  },
+    [ ANDROID_CONTROL_AE_STATE - ANDROID_CONTROL_START ] =
+    { "aeState",                       TYPE_BYTE   },
+    [ ANDROID_CONTROL_AF_STATE - ANDROID_CONTROL_START ] =
+    { "afState",                       TYPE_BYTE   },
+    [ ANDROID_CONTROL_AF_TRIGGER_ID - ANDROID_CONTROL_START ] =
+    { "afTriggerId",                   TYPE_INT32  },
+    [ ANDROID_CONTROL_AWB_STATE - ANDROID_CONTROL_START ] =
+    { "awbState",                      TYPE_BYTE   },
+    [ ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS - ANDROID_CONTROL_START ] =
+    { "availableHighSpeedVideoConfigurations",
+                                        TYPE_INT32  },
+    [ ANDROID_CONTROL_AE_LOCK_AVAILABLE - ANDROID_CONTROL_START ] =
+    { "aeLockAvailable",               TYPE_BYTE   },
+    [ ANDROID_CONTROL_AWB_LOCK_AVAILABLE - ANDROID_CONTROL_START ] =
+    { "awbLockAvailable",              TYPE_BYTE   },
+    [ ANDROID_CONTROL_AVAILABLE_MODES - ANDROID_CONTROL_START ] =
+    { "availableModes",                TYPE_BYTE   },
+    [ ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE - ANDROID_CONTROL_START ] =
+    { "postRawSensitivityBoostRange",  TYPE_INT32  },
+    [ ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST - ANDROID_CONTROL_START ] =
+    { "postRawSensitivityBoost",       TYPE_INT32  },
+    [ ANDROID_CONTROL_ENABLE_ZSL - ANDROID_CONTROL_START ] =
+    { "enableZsl",                     TYPE_BYTE   },
+    [ ANDROID_CONTROL_AF_SCENE_CHANGE - ANDROID_CONTROL_START ] =
+    { "afSceneChange",                 TYPE_BYTE   },
+};
+
+static tag_info_t android_demosaic[ANDROID_DEMOSAIC_END -
+        ANDROID_DEMOSAIC_START] = {
+    [ ANDROID_DEMOSAIC_MODE - ANDROID_DEMOSAIC_START ] =
+    { "mode",                          TYPE_BYTE   },
+};
+
+static tag_info_t android_edge[ANDROID_EDGE_END -
+        ANDROID_EDGE_START] = {
+    [ ANDROID_EDGE_MODE - ANDROID_EDGE_START ] =
+    { "mode",                          TYPE_BYTE   },
+    [ ANDROID_EDGE_STRENGTH - ANDROID_EDGE_START ] =
+    { "strength",                      TYPE_BYTE   },
+    [ ANDROID_EDGE_AVAILABLE_EDGE_MODES - ANDROID_EDGE_START ] =
+    { "availableEdgeModes",            TYPE_BYTE   },
+};
+
+static tag_info_t android_flash[ANDROID_FLASH_END -
+        ANDROID_FLASH_START] = {
+    [ ANDROID_FLASH_FIRING_POWER - ANDROID_FLASH_START ] =
+    { "firingPower",                   TYPE_BYTE   },
+    [ ANDROID_FLASH_FIRING_TIME - ANDROID_FLASH_START ] =
+    { "firingTime",                    TYPE_INT64  },
+    [ ANDROID_FLASH_MODE - ANDROID_FLASH_START ] =
+    { "mode",                          TYPE_BYTE   },
+    [ ANDROID_FLASH_COLOR_TEMPERATURE - ANDROID_FLASH_START ] =
+    { "colorTemperature",              TYPE_BYTE   },
+    [ ANDROID_FLASH_MAX_ENERGY - ANDROID_FLASH_START ] =
+    { "maxEnergy",                     TYPE_BYTE   },
+    [ ANDROID_FLASH_STATE - ANDROID_FLASH_START ] =
+    { "state",                         TYPE_BYTE   },
+};
+
+static tag_info_t android_flash_info[ANDROID_FLASH_INFO_END -
+        ANDROID_FLASH_INFO_START] = {
+    [ ANDROID_FLASH_INFO_AVAILABLE - ANDROID_FLASH_INFO_START ] =
+    { "available",                     TYPE_BYTE   },
+    [ ANDROID_FLASH_INFO_CHARGE_DURATION - ANDROID_FLASH_INFO_START ] =
+    { "chargeDuration",                TYPE_INT64  },
+};
+
+static tag_info_t android_hot_pixel[ANDROID_HOT_PIXEL_END -
+        ANDROID_HOT_PIXEL_START] = {
+    [ ANDROID_HOT_PIXEL_MODE - ANDROID_HOT_PIXEL_START ] =
+    { "mode",                          TYPE_BYTE   },
+    [ ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES - ANDROID_HOT_PIXEL_START ] =
+    { "availableHotPixelModes",        TYPE_BYTE   },
+};
+
+static tag_info_t android_jpeg[ANDROID_JPEG_END -
+        ANDROID_JPEG_START] = {
+    [ ANDROID_JPEG_GPS_COORDINATES - ANDROID_JPEG_START ] =
+    { "gpsCoordinates",                TYPE_DOUBLE },
+    [ ANDROID_JPEG_GPS_PROCESSING_METHOD - ANDROID_JPEG_START ] =
+    { "gpsProcessingMethod",           TYPE_BYTE   },
+    [ ANDROID_JPEG_GPS_TIMESTAMP - ANDROID_JPEG_START ] =
+    { "gpsTimestamp",                  TYPE_INT64  },
+    [ ANDROID_JPEG_ORIENTATION - ANDROID_JPEG_START ] =
+    { "orientation",                   TYPE_INT32  },
+    [ ANDROID_JPEG_QUALITY - ANDROID_JPEG_START ] =
+    { "quality",                       TYPE_BYTE   },
+    [ ANDROID_JPEG_THUMBNAIL_QUALITY - ANDROID_JPEG_START ] =
+    { "thumbnailQuality",              TYPE_BYTE   },
+    [ ANDROID_JPEG_THUMBNAIL_SIZE - ANDROID_JPEG_START ] =
+    { "thumbnailSize",                 TYPE_INT32  },
+    [ ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES - ANDROID_JPEG_START ] =
+    { "availableThumbnailSizes",       TYPE_INT32  },
+    [ ANDROID_JPEG_MAX_SIZE - ANDROID_JPEG_START ] =
+    { "maxSize",                       TYPE_INT32  },
+    [ ANDROID_JPEG_SIZE - ANDROID_JPEG_START ] =
+    { "size",                          TYPE_INT32  },
+};
+
+static tag_info_t android_lens[ANDROID_LENS_END -
+        ANDROID_LENS_START] = {
+    [ ANDROID_LENS_APERTURE - ANDROID_LENS_START ] =
+    { "aperture",                      TYPE_FLOAT  },
+    [ ANDROID_LENS_FILTER_DENSITY - ANDROID_LENS_START ] =
+    { "filterDensity",                 TYPE_FLOAT  },
+    [ ANDROID_LENS_FOCAL_LENGTH - ANDROID_LENS_START ] =
+    { "focalLength",                   TYPE_FLOAT  },
+    [ ANDROID_LENS_FOCUS_DISTANCE - ANDROID_LENS_START ] =
+    { "focusDistance",                 TYPE_FLOAT  },
+    [ ANDROID_LENS_OPTICAL_STABILIZATION_MODE - ANDROID_LENS_START ] =
+    { "opticalStabilizationMode",      TYPE_BYTE   },
+    [ ANDROID_LENS_FACING - ANDROID_LENS_START ] =
+    { "facing",                        TYPE_BYTE   },
+    [ ANDROID_LENS_POSE_ROTATION - ANDROID_LENS_START ] =
+    { "poseRotation",                  TYPE_FLOAT  },
+    [ ANDROID_LENS_POSE_TRANSLATION - ANDROID_LENS_START ] =
+    { "poseTranslation",               TYPE_FLOAT  },
+    [ ANDROID_LENS_FOCUS_RANGE - ANDROID_LENS_START ] =
+    { "focusRange",                    TYPE_FLOAT  },
+    [ ANDROID_LENS_STATE - ANDROID_LENS_START ] =
+    { "state",                         TYPE_BYTE   },
+    [ ANDROID_LENS_INTRINSIC_CALIBRATION - ANDROID_LENS_START ] =
+    { "intrinsicCalibration",          TYPE_FLOAT  },
+    [ ANDROID_LENS_RADIAL_DISTORTION - ANDROID_LENS_START ] =
+    { "radialDistortion",              TYPE_FLOAT  },
+    [ ANDROID_LENS_POSE_REFERENCE - ANDROID_LENS_START ] =
+    { "poseReference",                 TYPE_BYTE   },
+    [ ANDROID_LENS_DISTORTION - ANDROID_LENS_START ] =
+    { "distortion",                    TYPE_FLOAT  },
+};
+
+static tag_info_t android_lens_info[ANDROID_LENS_INFO_END -
+        ANDROID_LENS_INFO_START] = {
+    [ ANDROID_LENS_INFO_AVAILABLE_APERTURES - ANDROID_LENS_INFO_START ] =
+    { "availableApertures",            TYPE_FLOAT  },
+    [ ANDROID_LENS_INFO_AVAILABLE_FILTER_DENSITIES - ANDROID_LENS_INFO_START ] =
+    { "availableFilterDensities",      TYPE_FLOAT  },
+    [ ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS - ANDROID_LENS_INFO_START ] =
+    { "availableFocalLengths",         TYPE_FLOAT  },
+    [ ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION - ANDROID_LENS_INFO_START ] =
+    { "availableOpticalStabilization", TYPE_BYTE   },
+    [ ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE - ANDROID_LENS_INFO_START ] =
+    { "hyperfocalDistance",            TYPE_FLOAT  },
+    [ ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE - ANDROID_LENS_INFO_START ] =
+    { "minimumFocusDistance",          TYPE_FLOAT  },
+    [ ANDROID_LENS_INFO_SHADING_MAP_SIZE - ANDROID_LENS_INFO_START ] =
+    { "shadingMapSize",                TYPE_INT32  },
+    [ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION - ANDROID_LENS_INFO_START ] =
+    { "focusDistanceCalibration",      TYPE_BYTE   },
+};
+
+static tag_info_t android_noise_reduction[ANDROID_NOISE_REDUCTION_END -
+        ANDROID_NOISE_REDUCTION_START] = {
+    [ ANDROID_NOISE_REDUCTION_MODE - ANDROID_NOISE_REDUCTION_START ] =
+    { "mode",                          TYPE_BYTE   },
+    [ ANDROID_NOISE_REDUCTION_STRENGTH - ANDROID_NOISE_REDUCTION_START ] =
+    { "strength",                      TYPE_BYTE   },
+    [ ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES - ANDROID_NOISE_REDUCTION_START ] =
+    { "availableNoiseReductionModes",  TYPE_BYTE   },
+};
+
+static tag_info_t android_quirks[ANDROID_QUIRKS_END -
+        ANDROID_QUIRKS_START] = {
+    [ ANDROID_QUIRKS_METERING_CROP_REGION - ANDROID_QUIRKS_START ] =
+    { "meteringCropRegion",            TYPE_BYTE   },
+    [ ANDROID_QUIRKS_TRIGGER_AF_WITH_AUTO - ANDROID_QUIRKS_START ] =
+    { "triggerAfWithAuto",             TYPE_BYTE   },
+    [ ANDROID_QUIRKS_USE_ZSL_FORMAT - ANDROID_QUIRKS_START ] =
+    { "useZslFormat",                  TYPE_BYTE   },
+    [ ANDROID_QUIRKS_USE_PARTIAL_RESULT - ANDROID_QUIRKS_START ] =
+    { "usePartialResult",              TYPE_BYTE   },
+    [ ANDROID_QUIRKS_PARTIAL_RESULT - ANDROID_QUIRKS_START ] =
+    { "partialResult",                 TYPE_BYTE   },
+};
+
+static tag_info_t android_request[ANDROID_REQUEST_END -
+        ANDROID_REQUEST_START] = {
+    [ ANDROID_REQUEST_FRAME_COUNT - ANDROID_REQUEST_START ] =
+    { "frameCount",                    TYPE_INT32  },
+    [ ANDROID_REQUEST_ID - ANDROID_REQUEST_START ] =
+    { "id",                            TYPE_INT32  },
+    [ ANDROID_REQUEST_INPUT_STREAMS - ANDROID_REQUEST_START ] =
+    { "inputStreams",                  TYPE_INT32  },
+    [ ANDROID_REQUEST_METADATA_MODE - ANDROID_REQUEST_START ] =
+    { "metadataMode",                  TYPE_BYTE   },
+    [ ANDROID_REQUEST_OUTPUT_STREAMS - ANDROID_REQUEST_START ] =
+    { "outputStreams",                 TYPE_INT32  },
+    [ ANDROID_REQUEST_TYPE - ANDROID_REQUEST_START ] =
+    { "type",                          TYPE_BYTE   },
+    [ ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS - ANDROID_REQUEST_START ] =
+    { "maxNumOutputStreams",           TYPE_INT32  },
+    [ ANDROID_REQUEST_MAX_NUM_REPROCESS_STREAMS - ANDROID_REQUEST_START ] =
+    { "maxNumReprocessStreams",        TYPE_INT32  },
+    [ ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS - ANDROID_REQUEST_START ] =
+    { "maxNumInputStreams",            TYPE_INT32  },
+    [ ANDROID_REQUEST_PIPELINE_DEPTH - ANDROID_REQUEST_START ] =
+    { "pipelineDepth",                 TYPE_BYTE   },
+    [ ANDROID_REQUEST_PIPELINE_MAX_DEPTH - ANDROID_REQUEST_START ] =
+    { "pipelineMaxDepth",              TYPE_BYTE   },
+    [ ANDROID_REQUEST_PARTIAL_RESULT_COUNT - ANDROID_REQUEST_START ] =
+    { "partialResultCount",            TYPE_INT32  },
+    [ ANDROID_REQUEST_AVAILABLE_CAPABILITIES - ANDROID_REQUEST_START ] =
+    { "availableCapabilities",         TYPE_BYTE   },
+    [ ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS - ANDROID_REQUEST_START ] =
+    { "availableRequestKeys",          TYPE_INT32  },
+    [ ANDROID_REQUEST_AVAILABLE_RESULT_KEYS - ANDROID_REQUEST_START ] =
+    { "availableResultKeys",           TYPE_INT32  },
+    [ ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS - ANDROID_REQUEST_START ] =
+    { "availableCharacteristicsKeys",  TYPE_INT32  },
+    [ ANDROID_REQUEST_AVAILABLE_SESSION_KEYS - ANDROID_REQUEST_START ] =
+    { "availableSessionKeys",          TYPE_INT32  },
+    [ ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS - ANDROID_REQUEST_START ] =
+    { "availablePhysicalCameraRequestKeys",
+                                        TYPE_INT32  },
+};
+
+static tag_info_t android_scaler[ANDROID_SCALER_END -
+        ANDROID_SCALER_START] = {
+    [ ANDROID_SCALER_CROP_REGION - ANDROID_SCALER_START ] =
+    { "cropRegion",                    TYPE_INT32  },
+    [ ANDROID_SCALER_AVAILABLE_FORMATS - ANDROID_SCALER_START ] =
+    { "availableFormats",              TYPE_INT32  },
+    [ ANDROID_SCALER_AVAILABLE_JPEG_MIN_DURATIONS - ANDROID_SCALER_START ] =
+    { "availableJpegMinDurations",     TYPE_INT64  },
+    [ ANDROID_SCALER_AVAILABLE_JPEG_SIZES - ANDROID_SCALER_START ] =
+    { "availableJpegSizes",            TYPE_INT32  },
+    [ ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM - ANDROID_SCALER_START ] =
+    { "availableMaxDigitalZoom",       TYPE_FLOAT  },
+    [ ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS - ANDROID_SCALER_START ] =
+    { "availableProcessedMinDurations",
+                                        TYPE_INT64  },
+    [ ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES - ANDROID_SCALER_START ] =
+    { "availableProcessedSizes",       TYPE_INT32  },
+    [ ANDROID_SCALER_AVAILABLE_RAW_MIN_DURATIONS - ANDROID_SCALER_START ] =
+    { "availableRawMinDurations",      TYPE_INT64  },
+    [ ANDROID_SCALER_AVAILABLE_RAW_SIZES - ANDROID_SCALER_START ] =
+    { "availableRawSizes",             TYPE_INT32  },
+    [ ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP - ANDROID_SCALER_START ] =
+    { "availableInputOutputFormatsMap",
+                                        TYPE_INT32  },
+    [ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS - ANDROID_SCALER_START ] =
+    { "availableStreamConfigurations", TYPE_INT32  },
+    [ ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS - ANDROID_SCALER_START ] =
+    { "availableMinFrameDurations",    TYPE_INT64  },
+    [ ANDROID_SCALER_AVAILABLE_STALL_DURATIONS - ANDROID_SCALER_START ] =
+    { "availableStallDurations",       TYPE_INT64  },
+    [ ANDROID_SCALER_CROPPING_TYPE - ANDROID_SCALER_START ] =
+    { "croppingType",                  TYPE_BYTE   },
+};
+
+static tag_info_t android_sensor[ANDROID_SENSOR_END -
+        ANDROID_SENSOR_START] = {
+    [ ANDROID_SENSOR_EXPOSURE_TIME - ANDROID_SENSOR_START ] =
+    { "exposureTime",                  TYPE_INT64  },
+    [ ANDROID_SENSOR_FRAME_DURATION - ANDROID_SENSOR_START ] =
+    { "frameDuration",                 TYPE_INT64  },
+    [ ANDROID_SENSOR_SENSITIVITY - ANDROID_SENSOR_START ] =
+    { "sensitivity",                   TYPE_INT32  },
+    [ ANDROID_SENSOR_REFERENCE_ILLUMINANT1 - ANDROID_SENSOR_START ] =
+    { "referenceIlluminant1",          TYPE_BYTE   },
+    [ ANDROID_SENSOR_REFERENCE_ILLUMINANT2 - ANDROID_SENSOR_START ] =
+    { "referenceIlluminant2",          TYPE_BYTE   },
+    [ ANDROID_SENSOR_CALIBRATION_TRANSFORM1 - ANDROID_SENSOR_START ] =
+    { "calibrationTransform1",         TYPE_RATIONAL
+                },
+    [ ANDROID_SENSOR_CALIBRATION_TRANSFORM2 - ANDROID_SENSOR_START ] =
+    { "calibrationTransform2",         TYPE_RATIONAL
+                },
+    [ ANDROID_SENSOR_COLOR_TRANSFORM1 - ANDROID_SENSOR_START ] =
+    { "colorTransform1",               TYPE_RATIONAL
+                },
+    [ ANDROID_SENSOR_COLOR_TRANSFORM2 - ANDROID_SENSOR_START ] =
+    { "colorTransform2",               TYPE_RATIONAL
+                },
+    [ ANDROID_SENSOR_FORWARD_MATRIX1 - ANDROID_SENSOR_START ] =
+    { "forwardMatrix1",                TYPE_RATIONAL
+                },
+    [ ANDROID_SENSOR_FORWARD_MATRIX2 - ANDROID_SENSOR_START ] =
+    { "forwardMatrix2",                TYPE_RATIONAL
+                },
+    [ ANDROID_SENSOR_BASE_GAIN_FACTOR - ANDROID_SENSOR_START ] =
+    { "baseGainFactor",                TYPE_RATIONAL
+                },
+    [ ANDROID_SENSOR_BLACK_LEVEL_PATTERN - ANDROID_SENSOR_START ] =
+    { "blackLevelPattern",             TYPE_INT32  },
+    [ ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY - ANDROID_SENSOR_START ] =
+    { "maxAnalogSensitivity",          TYPE_INT32  },
+    [ ANDROID_SENSOR_ORIENTATION - ANDROID_SENSOR_START ] =
+    { "orientation",                   TYPE_INT32  },
+    [ ANDROID_SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS - ANDROID_SENSOR_START ] =
+    { "profileHueSatMapDimensions",    TYPE_INT32  },
+    [ ANDROID_SENSOR_TIMESTAMP - ANDROID_SENSOR_START ] =
+    { "timestamp",                     TYPE_INT64  },
+    [ ANDROID_SENSOR_TEMPERATURE - ANDROID_SENSOR_START ] =
+    { "temperature",                   TYPE_FLOAT  },
+    [ ANDROID_SENSOR_NEUTRAL_COLOR_POINT - ANDROID_SENSOR_START ] =
+    { "neutralColorPoint",             TYPE_RATIONAL
+                },
+    [ ANDROID_SENSOR_NOISE_PROFILE - ANDROID_SENSOR_START ] =
+    { "noiseProfile",                  TYPE_DOUBLE },
+    [ ANDROID_SENSOR_PROFILE_HUE_SAT_MAP - ANDROID_SENSOR_START ] =
+    { "profileHueSatMap",              TYPE_FLOAT  },
+    [ ANDROID_SENSOR_PROFILE_TONE_CURVE - ANDROID_SENSOR_START ] =
+    { "profileToneCurve",              TYPE_FLOAT  },
+    [ ANDROID_SENSOR_GREEN_SPLIT - ANDROID_SENSOR_START ] =
+    { "greenSplit",                    TYPE_FLOAT  },
+    [ ANDROID_SENSOR_TEST_PATTERN_DATA - ANDROID_SENSOR_START ] =
+    { "testPatternData",               TYPE_INT32  },
+    [ ANDROID_SENSOR_TEST_PATTERN_MODE - ANDROID_SENSOR_START ] =
+    { "testPatternMode",               TYPE_INT32  },
+    [ ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES - ANDROID_SENSOR_START ] =
+    { "availableTestPatternModes",     TYPE_INT32  },
+    [ ANDROID_SENSOR_ROLLING_SHUTTER_SKEW - ANDROID_SENSOR_START ] =
+    { "rollingShutterSkew",            TYPE_INT64  },
+    [ ANDROID_SENSOR_OPTICAL_BLACK_REGIONS - ANDROID_SENSOR_START ] =
+    { "opticalBlackRegions",           TYPE_INT32  },
+    [ ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL - ANDROID_SENSOR_START ] =
+    { "dynamicBlackLevel",             TYPE_FLOAT  },
+    [ ANDROID_SENSOR_DYNAMIC_WHITE_LEVEL - ANDROID_SENSOR_START ] =
+    { "dynamicWhiteLevel",             TYPE_INT32  },
+    [ ANDROID_SENSOR_OPAQUE_RAW_SIZE - ANDROID_SENSOR_START ] =
+    { "opaqueRawSize",                 TYPE_INT32  },
+};
+
+static tag_info_t android_sensor_info[ANDROID_SENSOR_INFO_END -
+        ANDROID_SENSOR_INFO_START] = {
+    [ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE - ANDROID_SENSOR_INFO_START ] =
+    { "activeArraySize",               TYPE_INT32  },
+    [ ANDROID_SENSOR_INFO_SENSITIVITY_RANGE - ANDROID_SENSOR_INFO_START ] =
+    { "sensitivityRange",              TYPE_INT32  },
+    [ ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT - ANDROID_SENSOR_INFO_START ] =
+    { "colorFilterArrangement",        TYPE_BYTE   },
+    [ ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE - ANDROID_SENSOR_INFO_START ] =
+    { "exposureTimeRange",             TYPE_INT64  },
+    [ ANDROID_SENSOR_INFO_MAX_FRAME_DURATION - ANDROID_SENSOR_INFO_START ] =
+    { "maxFrameDuration",              TYPE_INT64  },
+    [ ANDROID_SENSOR_INFO_PHYSICAL_SIZE - ANDROID_SENSOR_INFO_START ] =
+    { "physicalSize",                  TYPE_FLOAT  },
+    [ ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE - ANDROID_SENSOR_INFO_START ] =
+    { "pixelArraySize",                TYPE_INT32  },
+    [ ANDROID_SENSOR_INFO_WHITE_LEVEL - ANDROID_SENSOR_INFO_START ] =
+    { "whiteLevel",                    TYPE_INT32  },
+    [ ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE - ANDROID_SENSOR_INFO_START ] =
+    { "timestampSource",               TYPE_BYTE   },
+    [ ANDROID_SENSOR_INFO_LENS_SHADING_APPLIED - ANDROID_SENSOR_INFO_START ] =
+    { "lensShadingApplied",            TYPE_BYTE   },
+    [ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE - ANDROID_SENSOR_INFO_START ] =
+    { "preCorrectionActiveArraySize",  TYPE_INT32  },
+};
+
+static tag_info_t android_shading[ANDROID_SHADING_END -
+        ANDROID_SHADING_START] = {
+    [ ANDROID_SHADING_MODE - ANDROID_SHADING_START ] =
+    { "mode",                          TYPE_BYTE   },
+    [ ANDROID_SHADING_STRENGTH - ANDROID_SHADING_START ] =
+    { "strength",                      TYPE_BYTE   },
+    [ ANDROID_SHADING_AVAILABLE_MODES - ANDROID_SHADING_START ] =
+    { "availableModes",                TYPE_BYTE   },
+};
+
+static tag_info_t android_statistics[ANDROID_STATISTICS_END -
+        ANDROID_STATISTICS_START] = {
+    [ ANDROID_STATISTICS_FACE_DETECT_MODE - ANDROID_STATISTICS_START ] =
+    { "faceDetectMode",                TYPE_BYTE   },
+    [ ANDROID_STATISTICS_HISTOGRAM_MODE - ANDROID_STATISTICS_START ] =
+    { "histogramMode",                 TYPE_BYTE   },
+    [ ANDROID_STATISTICS_SHARPNESS_MAP_MODE - ANDROID_STATISTICS_START ] =
+    { "sharpnessMapMode",              TYPE_BYTE   },
+    [ ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE - ANDROID_STATISTICS_START ] =
+    { "hotPixelMapMode",               TYPE_BYTE   },
+    [ ANDROID_STATISTICS_FACE_IDS - ANDROID_STATISTICS_START ] =
+    { "faceIds",                       TYPE_INT32  },
+    [ ANDROID_STATISTICS_FACE_LANDMARKS - ANDROID_STATISTICS_START ] =
+    { "faceLandmarks",                 TYPE_INT32  },
+    [ ANDROID_STATISTICS_FACE_RECTANGLES - ANDROID_STATISTICS_START ] =
+    { "faceRectangles",                TYPE_INT32  },
+    [ ANDROID_STATISTICS_FACE_SCORES - ANDROID_STATISTICS_START ] =
+    { "faceScores",                    TYPE_BYTE   },
+    [ ANDROID_STATISTICS_HISTOGRAM - ANDROID_STATISTICS_START ] =
+    { "histogram",                     TYPE_INT32  },
+    [ ANDROID_STATISTICS_SHARPNESS_MAP - ANDROID_STATISTICS_START ] =
+    { "sharpnessMap",                  TYPE_INT32  },
+    [ ANDROID_STATISTICS_LENS_SHADING_CORRECTION_MAP - ANDROID_STATISTICS_START ] =
+    { "lensShadingCorrectionMap",      TYPE_BYTE   },
+    [ ANDROID_STATISTICS_LENS_SHADING_MAP - ANDROID_STATISTICS_START ] =
+    { "lensShadingMap",                TYPE_FLOAT  },
+    [ ANDROID_STATISTICS_PREDICTED_COLOR_GAINS - ANDROID_STATISTICS_START ] =
+    { "predictedColorGains",           TYPE_FLOAT  },
+    [ ANDROID_STATISTICS_PREDICTED_COLOR_TRANSFORM - ANDROID_STATISTICS_START ] =
+    { "predictedColorTransform",       TYPE_RATIONAL
+                },
+    [ ANDROID_STATISTICS_SCENE_FLICKER - ANDROID_STATISTICS_START ] =
+    { "sceneFlicker",                  TYPE_BYTE   },
+    [ ANDROID_STATISTICS_HOT_PIXEL_MAP - ANDROID_STATISTICS_START ] =
+    { "hotPixelMap",                   TYPE_INT32  },
+    [ ANDROID_STATISTICS_LENS_SHADING_MAP_MODE - ANDROID_STATISTICS_START ] =
+    { "lensShadingMapMode",            TYPE_BYTE   },
+    [ ANDROID_STATISTICS_OIS_DATA_MODE - ANDROID_STATISTICS_START ] =
+    { "oisDataMode",                   TYPE_BYTE   },
+    [ ANDROID_STATISTICS_OIS_TIMESTAMPS - ANDROID_STATISTICS_START ] =
+    { "oisTimestamps",                 TYPE_INT64  },
+    [ ANDROID_STATISTICS_OIS_X_SHIFTS - ANDROID_STATISTICS_START ] =
+    { "oisXShifts",                    TYPE_FLOAT  },
+    [ ANDROID_STATISTICS_OIS_Y_SHIFTS - ANDROID_STATISTICS_START ] =
+    { "oisYShifts",                    TYPE_FLOAT  },
+};
+
+static tag_info_t android_statistics_info[ANDROID_STATISTICS_INFO_END -
+        ANDROID_STATISTICS_INFO_START] = {
+    [ ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES - ANDROID_STATISTICS_INFO_START ] =
+    { "availableFaceDetectModes",      TYPE_BYTE   },
+    [ ANDROID_STATISTICS_INFO_HISTOGRAM_BUCKET_COUNT - ANDROID_STATISTICS_INFO_START ] =
+    { "histogramBucketCount",          TYPE_INT32  },
+    [ ANDROID_STATISTICS_INFO_MAX_FACE_COUNT - ANDROID_STATISTICS_INFO_START ] =
+    { "maxFaceCount",                  TYPE_INT32  },
+    [ ANDROID_STATISTICS_INFO_MAX_HISTOGRAM_COUNT - ANDROID_STATISTICS_INFO_START ] =
+    { "maxHistogramCount",             TYPE_INT32  },
+    [ ANDROID_STATISTICS_INFO_MAX_SHARPNESS_MAP_VALUE - ANDROID_STATISTICS_INFO_START ] =
+    { "maxSharpnessMapValue",          TYPE_INT32  },
+    [ ANDROID_STATISTICS_INFO_SHARPNESS_MAP_SIZE - ANDROID_STATISTICS_INFO_START ] =
+    { "sharpnessMapSize",              TYPE_INT32  },
+    [ ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES - ANDROID_STATISTICS_INFO_START ] =
+    { "availableHotPixelMapModes",     TYPE_BYTE   },
+    [ ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES - ANDROID_STATISTICS_INFO_START ] =
+    { "availableLensShadingMapModes",  TYPE_BYTE   },
+    [ ANDROID_STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES - ANDROID_STATISTICS_INFO_START ] =
+    { "availableOisDataModes",         TYPE_BYTE   },
+};
+
+static tag_info_t android_tonemap[ANDROID_TONEMAP_END -
+        ANDROID_TONEMAP_START] = {
+    [ ANDROID_TONEMAP_CURVE_BLUE - ANDROID_TONEMAP_START ] =
+    { "curveBlue",                     TYPE_FLOAT  },
+    [ ANDROID_TONEMAP_CURVE_GREEN - ANDROID_TONEMAP_START ] =
+    { "curveGreen",                    TYPE_FLOAT  },
+    [ ANDROID_TONEMAP_CURVE_RED - ANDROID_TONEMAP_START ] =
+    { "curveRed",                      TYPE_FLOAT  },
+    [ ANDROID_TONEMAP_MODE - ANDROID_TONEMAP_START ] =
+    { "mode",                          TYPE_BYTE   },
+    [ ANDROID_TONEMAP_MAX_CURVE_POINTS - ANDROID_TONEMAP_START ] =
+    { "maxCurvePoints",                TYPE_INT32  },
+    [ ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES - ANDROID_TONEMAP_START ] =
+    { "availableToneMapModes",         TYPE_BYTE   },
+    [ ANDROID_TONEMAP_GAMMA - ANDROID_TONEMAP_START ] =
+    { "gamma",                         TYPE_FLOAT  },
+    [ ANDROID_TONEMAP_PRESET_CURVE - ANDROID_TONEMAP_START ] =
+    { "presetCurve",                   TYPE_BYTE   },
+};
+
+static tag_info_t android_led[ANDROID_LED_END -
+        ANDROID_LED_START] = {
+    [ ANDROID_LED_TRANSMIT - ANDROID_LED_START ] =
+    { "transmit",                      TYPE_BYTE   },
+    [ ANDROID_LED_AVAILABLE_LEDS - ANDROID_LED_START ] =
+    { "availableLeds",                 TYPE_BYTE   },
+};
+
+static tag_info_t android_info[ANDROID_INFO_END -
+        ANDROID_INFO_START] = {
+    [ ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL - ANDROID_INFO_START ] =
+    { "supportedHardwareLevel",        TYPE_BYTE   },
+    [ ANDROID_INFO_VERSION - ANDROID_INFO_START ] =
+    { "version",                       TYPE_BYTE   },
+};
+
+static tag_info_t android_black_level[ANDROID_BLACK_LEVEL_END -
+        ANDROID_BLACK_LEVEL_START] = {
+    [ ANDROID_BLACK_LEVEL_LOCK - ANDROID_BLACK_LEVEL_START ] =
+    { "lock",                          TYPE_BYTE   },
+};
+
+static tag_info_t android_sync[ANDROID_SYNC_END -
+        ANDROID_SYNC_START] = {
+    [ ANDROID_SYNC_FRAME_NUMBER - ANDROID_SYNC_START ] =
+    { "frameNumber",                   TYPE_INT64  },
+    [ ANDROID_SYNC_MAX_LATENCY - ANDROID_SYNC_START ] =
+    { "maxLatency",                    TYPE_INT32  },
+};
+
+static tag_info_t android_reprocess[ANDROID_REPROCESS_END -
+        ANDROID_REPROCESS_START] = {
+    [ ANDROID_REPROCESS_EFFECTIVE_EXPOSURE_FACTOR - ANDROID_REPROCESS_START ] =
+    { "effectiveExposureFactor",       TYPE_FLOAT  },
+    [ ANDROID_REPROCESS_MAX_CAPTURE_STALL - ANDROID_REPROCESS_START ] =
+    { "maxCaptureStall",               TYPE_INT32  },
+};
+
+static tag_info_t android_depth[ANDROID_DEPTH_END -
+        ANDROID_DEPTH_START] = {
+    [ ANDROID_DEPTH_MAX_DEPTH_SAMPLES - ANDROID_DEPTH_START ] =
+    { "maxDepthSamples",               TYPE_INT32  },
+    [ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS - ANDROID_DEPTH_START ] =
+    { "availableDepthStreamConfigurations",
+                                        TYPE_INT32  },
+    [ ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS - ANDROID_DEPTH_START ] =
+    { "availableDepthMinFrameDurations",
+                                        TYPE_INT64  },
+    [ ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS - ANDROID_DEPTH_START ] =
+    { "availableDepthStallDurations",  TYPE_INT64  },
+    [ ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE - ANDROID_DEPTH_START ] =
+    { "depthIsExclusive",              TYPE_BYTE   },
+};
+
+static tag_info_t android_logical_multi_camera[ANDROID_LOGICAL_MULTI_CAMERA_END -
+        ANDROID_LOGICAL_MULTI_CAMERA_START] = {
+    [ ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS - ANDROID_LOGICAL_MULTI_CAMERA_START ] =
+    { "physicalIds",                   TYPE_BYTE   },
+    [ ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE - ANDROID_LOGICAL_MULTI_CAMERA_START ] =
+    { "sensorSyncType",                TYPE_BYTE   },
+};
+
+static tag_info_t android_distortion_correction[ANDROID_DISTORTION_CORRECTION_END -
+        ANDROID_DISTORTION_CORRECTION_START] = {
+    [ ANDROID_DISTORTION_CORRECTION_MODE - ANDROID_DISTORTION_CORRECTION_START ] =
+    { "mode",                          TYPE_BYTE   },
+    [ ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES - ANDROID_DISTORTION_CORRECTION_START ] =
+    { "availableModes",                TYPE_BYTE   },
+};
+
+
+tag_info_t *tag_info[ANDROID_SECTION_COUNT] = {
+    android_color_correction,
+    android_control,
+    android_demosaic,
+    android_edge,
+    android_flash,
+    android_flash_info,
+    android_hot_pixel,
+    android_jpeg,
+    android_lens,
+    android_lens_info,
+    android_noise_reduction,
+    android_quirks,
+    android_request,
+    android_scaler,
+    android_sensor,
+    android_sensor_info,
+    android_shading,
+    android_statistics,
+    android_statistics_info,
+    android_tonemap,
+    android_led,
+    android_info,
+    android_black_level,
+    android_sync,
+    android_reprocess,
+    android_depth,
+    android_logical_multi_camera,
+    android_distortion_correction,
+};
+
+int camera_metadata_enum_snprint(uint32_t tag,
+                                 uint32_t value,
+                                 char *dst,
+                                 size_t size) {
+    const char *msg = "error: not an enum";
+    int ret = -1;
+
+    switch(tag) {
+        case ANDROID_COLOR_CORRECTION_MODE: {
+            switch (value) {
+                case ANDROID_COLOR_CORRECTION_MODE_TRANSFORM_MATRIX:
+                    msg = "TRANSFORM_MATRIX";
+                    ret = 0;
+                    break;
+                case ANDROID_COLOR_CORRECTION_MODE_FAST:
+                    msg = "FAST";
+                    ret = 0;
+                    break;
+                case ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY:
+                    msg = "HIGH_QUALITY";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_COLOR_CORRECTION_TRANSFORM: {
+            break;
+        }
+        case ANDROID_COLOR_CORRECTION_GAINS: {
+            break;
+        }
+        case ANDROID_COLOR_CORRECTION_ABERRATION_MODE: {
+            switch (value) {
+                case ANDROID_COLOR_CORRECTION_ABERRATION_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_COLOR_CORRECTION_ABERRATION_MODE_FAST:
+                    msg = "FAST";
+                    ret = 0;
+                    break;
+                case ANDROID_COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY:
+                    msg = "HIGH_QUALITY";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES: {
+            break;
+        }
+
+        case ANDROID_CONTROL_AE_ANTIBANDING_MODE: {
+            switch (value) {
+                case ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ:
+                    msg = "50HZ";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ:
+                    msg = "60HZ";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO:
+                    msg = "AUTO";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION: {
+            break;
+        }
+        case ANDROID_CONTROL_AE_LOCK: {
+            switch (value) {
+                case ANDROID_CONTROL_AE_LOCK_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_LOCK_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AE_MODE: {
+            switch (value) {
+                case ANDROID_CONTROL_AE_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_MODE_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH:
+                    msg = "ON_AUTO_FLASH";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH:
+                    msg = "ON_ALWAYS_FLASH";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE:
+                    msg = "ON_AUTO_FLASH_REDEYE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_MODE_ON_EXTERNAL_FLASH:
+                    msg = "ON_EXTERNAL_FLASH";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AE_REGIONS: {
+            break;
+        }
+        case ANDROID_CONTROL_AE_TARGET_FPS_RANGE: {
+            break;
+        }
+        case ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER: {
+            switch (value) {
+                case ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE:
+                    msg = "IDLE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_START:
+                    msg = "START";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL:
+                    msg = "CANCEL";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AF_MODE: {
+            switch (value) {
+                case ANDROID_CONTROL_AF_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_MODE_AUTO:
+                    msg = "AUTO";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_MODE_MACRO:
+                    msg = "MACRO";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+                    msg = "CONTINUOUS_VIDEO";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+                    msg = "CONTINUOUS_PICTURE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_MODE_EDOF:
+                    msg = "EDOF";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AF_REGIONS: {
+            break;
+        }
+        case ANDROID_CONTROL_AF_TRIGGER: {
+            switch (value) {
+                case ANDROID_CONTROL_AF_TRIGGER_IDLE:
+                    msg = "IDLE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_TRIGGER_START:
+                    msg = "START";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_TRIGGER_CANCEL:
+                    msg = "CANCEL";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AWB_LOCK: {
+            switch (value) {
+                case ANDROID_CONTROL_AWB_LOCK_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_LOCK_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AWB_MODE: {
+            switch (value) {
+                case ANDROID_CONTROL_AWB_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_MODE_AUTO:
+                    msg = "AUTO";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_MODE_INCANDESCENT:
+                    msg = "INCANDESCENT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_MODE_FLUORESCENT:
+                    msg = "FLUORESCENT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_MODE_WARM_FLUORESCENT:
+                    msg = "WARM_FLUORESCENT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_MODE_DAYLIGHT:
+                    msg = "DAYLIGHT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT:
+                    msg = "CLOUDY_DAYLIGHT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_MODE_TWILIGHT:
+                    msg = "TWILIGHT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_MODE_SHADE:
+                    msg = "SHADE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AWB_REGIONS: {
+            break;
+        }
+        case ANDROID_CONTROL_CAPTURE_INTENT: {
+            switch (value) {
+                case ANDROID_CONTROL_CAPTURE_INTENT_CUSTOM:
+                    msg = "CUSTOM";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW:
+                    msg = "PREVIEW";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE:
+                    msg = "STILL_CAPTURE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD:
+                    msg = "VIDEO_RECORD";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT:
+                    msg = "VIDEO_SNAPSHOT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG:
+                    msg = "ZERO_SHUTTER_LAG";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_CAPTURE_INTENT_MANUAL:
+                    msg = "MANUAL";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_CAPTURE_INTENT_MOTION_TRACKING:
+                    msg = "MOTION_TRACKING";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_EFFECT_MODE: {
+            switch (value) {
+                case ANDROID_CONTROL_EFFECT_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_EFFECT_MODE_MONO:
+                    msg = "MONO";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_EFFECT_MODE_NEGATIVE:
+                    msg = "NEGATIVE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_EFFECT_MODE_SOLARIZE:
+                    msg = "SOLARIZE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_EFFECT_MODE_SEPIA:
+                    msg = "SEPIA";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_EFFECT_MODE_POSTERIZE:
+                    msg = "POSTERIZE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_EFFECT_MODE_WHITEBOARD:
+                    msg = "WHITEBOARD";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_EFFECT_MODE_BLACKBOARD:
+                    msg = "BLACKBOARD";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_EFFECT_MODE_AQUA:
+                    msg = "AQUA";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_MODE: {
+            switch (value) {
+                case ANDROID_CONTROL_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_MODE_AUTO:
+                    msg = "AUTO";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_MODE_USE_SCENE_MODE:
+                    msg = "USE_SCENE_MODE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_MODE_OFF_KEEP_STATE:
+                    msg = "OFF_KEEP_STATE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_SCENE_MODE: {
+            switch (value) {
+                case ANDROID_CONTROL_SCENE_MODE_DISABLED:
+                    msg = "DISABLED";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY:
+                    msg = "FACE_PRIORITY";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_ACTION:
+                    msg = "ACTION";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_PORTRAIT:
+                    msg = "PORTRAIT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_LANDSCAPE:
+                    msg = "LANDSCAPE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_NIGHT:
+                    msg = "NIGHT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_NIGHT_PORTRAIT:
+                    msg = "NIGHT_PORTRAIT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_THEATRE:
+                    msg = "THEATRE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_BEACH:
+                    msg = "BEACH";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_SNOW:
+                    msg = "SNOW";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_SUNSET:
+                    msg = "SUNSET";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_STEADYPHOTO:
+                    msg = "STEADYPHOTO";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_FIREWORKS:
+                    msg = "FIREWORKS";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_SPORTS:
+                    msg = "SPORTS";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_PARTY:
+                    msg = "PARTY";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_CANDLELIGHT:
+                    msg = "CANDLELIGHT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_BARCODE:
+                    msg = "BARCODE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO:
+                    msg = "HIGH_SPEED_VIDEO";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_HDR:
+                    msg = "HDR";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY_LOW_LIGHT:
+                    msg = "FACE_PRIORITY_LOW_LIGHT";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_DEVICE_CUSTOM_START:
+                    msg = "DEVICE_CUSTOM_START";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_SCENE_MODE_DEVICE_CUSTOM_END:
+                    msg = "DEVICE_CUSTOM_END";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_VIDEO_STABILIZATION_MODE: {
+            switch (value) {
+                case ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES: {
+            break;
+        }
+        case ANDROID_CONTROL_AE_AVAILABLE_MODES: {
+            break;
+        }
+        case ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES: {
+            break;
+        }
+        case ANDROID_CONTROL_AE_COMPENSATION_RANGE: {
+            break;
+        }
+        case ANDROID_CONTROL_AE_COMPENSATION_STEP: {
+            break;
+        }
+        case ANDROID_CONTROL_AF_AVAILABLE_MODES: {
+            break;
+        }
+        case ANDROID_CONTROL_AVAILABLE_EFFECTS: {
+            break;
+        }
+        case ANDROID_CONTROL_AVAILABLE_SCENE_MODES: {
+            break;
+        }
+        case ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES: {
+            break;
+        }
+        case ANDROID_CONTROL_AWB_AVAILABLE_MODES: {
+            break;
+        }
+        case ANDROID_CONTROL_MAX_REGIONS: {
+            break;
+        }
+        case ANDROID_CONTROL_SCENE_MODE_OVERRIDES: {
+            break;
+        }
+        case ANDROID_CONTROL_AE_PRECAPTURE_ID: {
+            break;
+        }
+        case ANDROID_CONTROL_AE_STATE: {
+            switch (value) {
+                case ANDROID_CONTROL_AE_STATE_INACTIVE:
+                    msg = "INACTIVE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_STATE_SEARCHING:
+                    msg = "SEARCHING";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_STATE_CONVERGED:
+                    msg = "CONVERGED";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_STATE_LOCKED:
+                    msg = "LOCKED";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED:
+                    msg = "FLASH_REQUIRED";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_STATE_PRECAPTURE:
+                    msg = "PRECAPTURE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AF_STATE: {
+            switch (value) {
+                case ANDROID_CONTROL_AF_STATE_INACTIVE:
+                    msg = "INACTIVE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN:
+                    msg = "PASSIVE_SCAN";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED:
+                    msg = "PASSIVE_FOCUSED";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN:
+                    msg = "ACTIVE_SCAN";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED:
+                    msg = "FOCUSED_LOCKED";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED:
+                    msg = "NOT_FOCUSED_LOCKED";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_STATE_PASSIVE_UNFOCUSED:
+                    msg = "PASSIVE_UNFOCUSED";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AF_TRIGGER_ID: {
+            break;
+        }
+        case ANDROID_CONTROL_AWB_STATE: {
+            switch (value) {
+                case ANDROID_CONTROL_AWB_STATE_INACTIVE:
+                    msg = "INACTIVE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_STATE_SEARCHING:
+                    msg = "SEARCHING";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_STATE_CONVERGED:
+                    msg = "CONVERGED";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_STATE_LOCKED:
+                    msg = "LOCKED";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS: {
+            break;
+        }
+        case ANDROID_CONTROL_AE_LOCK_AVAILABLE: {
+            switch (value) {
+                case ANDROID_CONTROL_AE_LOCK_AVAILABLE_FALSE:
+                    msg = "FALSE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE:
+                    msg = "TRUE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AWB_LOCK_AVAILABLE: {
+            switch (value) {
+                case ANDROID_CONTROL_AWB_LOCK_AVAILABLE_FALSE:
+                    msg = "FALSE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE:
+                    msg = "TRUE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AVAILABLE_MODES: {
+            break;
+        }
+        case ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE: {
+            break;
+        }
+        case ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST: {
+            break;
+        }
+        case ANDROID_CONTROL_ENABLE_ZSL: {
+            switch (value) {
+                case ANDROID_CONTROL_ENABLE_ZSL_FALSE:
+                    msg = "FALSE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_ENABLE_ZSL_TRUE:
+                    msg = "TRUE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_CONTROL_AF_SCENE_CHANGE: {
+            switch (value) {
+                case ANDROID_CONTROL_AF_SCENE_CHANGE_NOT_DETECTED:
+                    msg = "NOT_DETECTED";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_AF_SCENE_CHANGE_DETECTED:
+                    msg = "DETECTED";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_DEMOSAIC_MODE: {
+            switch (value) {
+                case ANDROID_DEMOSAIC_MODE_FAST:
+                    msg = "FAST";
+                    ret = 0;
+                    break;
+                case ANDROID_DEMOSAIC_MODE_HIGH_QUALITY:
+                    msg = "HIGH_QUALITY";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_EDGE_MODE: {
+            switch (value) {
+                case ANDROID_EDGE_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_EDGE_MODE_FAST:
+                    msg = "FAST";
+                    ret = 0;
+                    break;
+                case ANDROID_EDGE_MODE_HIGH_QUALITY:
+                    msg = "HIGH_QUALITY";
+                    ret = 0;
+                    break;
+                case ANDROID_EDGE_MODE_ZERO_SHUTTER_LAG:
+                    msg = "ZERO_SHUTTER_LAG";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_EDGE_STRENGTH: {
+            break;
+        }
+        case ANDROID_EDGE_AVAILABLE_EDGE_MODES: {
+            break;
+        }
+
+        case ANDROID_FLASH_FIRING_POWER: {
+            break;
+        }
+        case ANDROID_FLASH_FIRING_TIME: {
+            break;
+        }
+        case ANDROID_FLASH_MODE: {
+            switch (value) {
+                case ANDROID_FLASH_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_FLASH_MODE_SINGLE:
+                    msg = "SINGLE";
+                    ret = 0;
+                    break;
+                case ANDROID_FLASH_MODE_TORCH:
+                    msg = "TORCH";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_FLASH_COLOR_TEMPERATURE: {
+            break;
+        }
+        case ANDROID_FLASH_MAX_ENERGY: {
+            break;
+        }
+        case ANDROID_FLASH_STATE: {
+            switch (value) {
+                case ANDROID_FLASH_STATE_UNAVAILABLE:
+                    msg = "UNAVAILABLE";
+                    ret = 0;
+                    break;
+                case ANDROID_FLASH_STATE_CHARGING:
+                    msg = "CHARGING";
+                    ret = 0;
+                    break;
+                case ANDROID_FLASH_STATE_READY:
+                    msg = "READY";
+                    ret = 0;
+                    break;
+                case ANDROID_FLASH_STATE_FIRED:
+                    msg = "FIRED";
+                    ret = 0;
+                    break;
+                case ANDROID_FLASH_STATE_PARTIAL:
+                    msg = "PARTIAL";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_FLASH_INFO_AVAILABLE: {
+            switch (value) {
+                case ANDROID_FLASH_INFO_AVAILABLE_FALSE:
+                    msg = "FALSE";
+                    ret = 0;
+                    break;
+                case ANDROID_FLASH_INFO_AVAILABLE_TRUE:
+                    msg = "TRUE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_FLASH_INFO_CHARGE_DURATION: {
+            break;
+        }
+
+        case ANDROID_HOT_PIXEL_MODE: {
+            switch (value) {
+                case ANDROID_HOT_PIXEL_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_HOT_PIXEL_MODE_FAST:
+                    msg = "FAST";
+                    ret = 0;
+                    break;
+                case ANDROID_HOT_PIXEL_MODE_HIGH_QUALITY:
+                    msg = "HIGH_QUALITY";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES: {
+            break;
+        }
+
+        case ANDROID_JPEG_GPS_COORDINATES: {
+            break;
+        }
+        case ANDROID_JPEG_GPS_PROCESSING_METHOD: {
+            break;
+        }
+        case ANDROID_JPEG_GPS_TIMESTAMP: {
+            break;
+        }
+        case ANDROID_JPEG_ORIENTATION: {
+            break;
+        }
+        case ANDROID_JPEG_QUALITY: {
+            break;
+        }
+        case ANDROID_JPEG_THUMBNAIL_QUALITY: {
+            break;
+        }
+        case ANDROID_JPEG_THUMBNAIL_SIZE: {
+            break;
+        }
+        case ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES: {
+            break;
+        }
+        case ANDROID_JPEG_MAX_SIZE: {
+            break;
+        }
+        case ANDROID_JPEG_SIZE: {
+            break;
+        }
+
+        case ANDROID_LENS_APERTURE: {
+            break;
+        }
+        case ANDROID_LENS_FILTER_DENSITY: {
+            break;
+        }
+        case ANDROID_LENS_FOCAL_LENGTH: {
+            break;
+        }
+        case ANDROID_LENS_FOCUS_DISTANCE: {
+            break;
+        }
+        case ANDROID_LENS_OPTICAL_STABILIZATION_MODE: {
+            switch (value) {
+                case ANDROID_LENS_OPTICAL_STABILIZATION_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_LENS_OPTICAL_STABILIZATION_MODE_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_LENS_FACING: {
+            switch (value) {
+                case ANDROID_LENS_FACING_FRONT:
+                    msg = "FRONT";
+                    ret = 0;
+                    break;
+                case ANDROID_LENS_FACING_BACK:
+                    msg = "BACK";
+                    ret = 0;
+                    break;
+                case ANDROID_LENS_FACING_EXTERNAL:
+                    msg = "EXTERNAL";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_LENS_POSE_ROTATION: {
+            break;
+        }
+        case ANDROID_LENS_POSE_TRANSLATION: {
+            break;
+        }
+        case ANDROID_LENS_FOCUS_RANGE: {
+            break;
+        }
+        case ANDROID_LENS_STATE: {
+            switch (value) {
+                case ANDROID_LENS_STATE_STATIONARY:
+                    msg = "STATIONARY";
+                    ret = 0;
+                    break;
+                case ANDROID_LENS_STATE_MOVING:
+                    msg = "MOVING";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_LENS_INTRINSIC_CALIBRATION: {
+            break;
+        }
+        case ANDROID_LENS_RADIAL_DISTORTION: {
+            break;
+        }
+        case ANDROID_LENS_POSE_REFERENCE: {
+            switch (value) {
+                case ANDROID_LENS_POSE_REFERENCE_PRIMARY_CAMERA:
+                    msg = "PRIMARY_CAMERA";
+                    ret = 0;
+                    break;
+                case ANDROID_LENS_POSE_REFERENCE_GYROSCOPE:
+                    msg = "GYROSCOPE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_LENS_DISTORTION: {
+            break;
+        }
+
+        case ANDROID_LENS_INFO_AVAILABLE_APERTURES: {
+            break;
+        }
+        case ANDROID_LENS_INFO_AVAILABLE_FILTER_DENSITIES: {
+            break;
+        }
+        case ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS: {
+            break;
+        }
+        case ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION: {
+            break;
+        }
+        case ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE: {
+            break;
+        }
+        case ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE: {
+            break;
+        }
+        case ANDROID_LENS_INFO_SHADING_MAP_SIZE: {
+            break;
+        }
+        case ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION: {
+            switch (value) {
+                case ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED:
+                    msg = "UNCALIBRATED";
+                    ret = 0;
+                    break;
+                case ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE:
+                    msg = "APPROXIMATE";
+                    ret = 0;
+                    break;
+                case ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED:
+                    msg = "CALIBRATED";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_NOISE_REDUCTION_MODE: {
+            switch (value) {
+                case ANDROID_NOISE_REDUCTION_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_NOISE_REDUCTION_MODE_FAST:
+                    msg = "FAST";
+                    ret = 0;
+                    break;
+                case ANDROID_NOISE_REDUCTION_MODE_HIGH_QUALITY:
+                    msg = "HIGH_QUALITY";
+                    ret = 0;
+                    break;
+                case ANDROID_NOISE_REDUCTION_MODE_MINIMAL:
+                    msg = "MINIMAL";
+                    ret = 0;
+                    break;
+                case ANDROID_NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG:
+                    msg = "ZERO_SHUTTER_LAG";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_NOISE_REDUCTION_STRENGTH: {
+            break;
+        }
+        case ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES: {
+            break;
+        }
+
+        case ANDROID_QUIRKS_METERING_CROP_REGION: {
+            break;
+        }
+        case ANDROID_QUIRKS_TRIGGER_AF_WITH_AUTO: {
+            break;
+        }
+        case ANDROID_QUIRKS_USE_ZSL_FORMAT: {
+            break;
+        }
+        case ANDROID_QUIRKS_USE_PARTIAL_RESULT: {
+            break;
+        }
+        case ANDROID_QUIRKS_PARTIAL_RESULT: {
+            switch (value) {
+                case ANDROID_QUIRKS_PARTIAL_RESULT_FINAL:
+                    msg = "FINAL";
+                    ret = 0;
+                    break;
+                case ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL:
+                    msg = "PARTIAL";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_REQUEST_FRAME_COUNT: {
+            break;
+        }
+        case ANDROID_REQUEST_ID: {
+            break;
+        }
+        case ANDROID_REQUEST_INPUT_STREAMS: {
+            break;
+        }
+        case ANDROID_REQUEST_METADATA_MODE: {
+            switch (value) {
+                case ANDROID_REQUEST_METADATA_MODE_NONE:
+                    msg = "NONE";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_METADATA_MODE_FULL:
+                    msg = "FULL";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_REQUEST_OUTPUT_STREAMS: {
+            break;
+        }
+        case ANDROID_REQUEST_TYPE: {
+            switch (value) {
+                case ANDROID_REQUEST_TYPE_CAPTURE:
+                    msg = "CAPTURE";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_TYPE_REPROCESS:
+                    msg = "REPROCESS";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_REQUEST_MAX_NUM_OUTPUT_STREAMS: {
+            break;
+        }
+        case ANDROID_REQUEST_MAX_NUM_REPROCESS_STREAMS: {
+            break;
+        }
+        case ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS: {
+            break;
+        }
+        case ANDROID_REQUEST_PIPELINE_DEPTH: {
+            break;
+        }
+        case ANDROID_REQUEST_PIPELINE_MAX_DEPTH: {
+            break;
+        }
+        case ANDROID_REQUEST_PARTIAL_RESULT_COUNT: {
+            break;
+        }
+        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES: {
+            switch (value) {
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE:
+                    msg = "BACKWARD_COMPATIBLE";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR:
+                    msg = "MANUAL_SENSOR";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING:
+                    msg = "MANUAL_POST_PROCESSING";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW:
+                    msg = "RAW";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING:
+                    msg = "PRIVATE_REPROCESSING";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS:
+                    msg = "READ_SENSOR_SETTINGS";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE:
+                    msg = "BURST_CAPTURE";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING:
+                    msg = "YUV_REPROCESSING";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT:
+                    msg = "DEPTH_OUTPUT";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO:
+                    msg = "CONSTRAINED_HIGH_SPEED_VIDEO";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING:
+                    msg = "MOTION_TRACKING";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA:
+                    msg = "LOGICAL_MULTI_CAMERA";
+                    ret = 0;
+                    break;
+                case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME:
+                    msg = "MONOCHROME";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS: {
+            break;
+        }
+        case ANDROID_REQUEST_AVAILABLE_RESULT_KEYS: {
+            break;
+        }
+        case ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS: {
+            break;
+        }
+        case ANDROID_REQUEST_AVAILABLE_SESSION_KEYS: {
+            break;
+        }
+        case ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS: {
+            break;
+        }
+
+        case ANDROID_SCALER_CROP_REGION: {
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_FORMATS: {
+            switch (value) {
+                case ANDROID_SCALER_AVAILABLE_FORMATS_RAW16:
+                    msg = "RAW16";
+                    ret = 0;
+                    break;
+                case ANDROID_SCALER_AVAILABLE_FORMATS_RAW_OPAQUE:
+                    msg = "RAW_OPAQUE";
+                    ret = 0;
+                    break;
+                case ANDROID_SCALER_AVAILABLE_FORMATS_YV12:
+                    msg = "YV12";
+                    ret = 0;
+                    break;
+                case ANDROID_SCALER_AVAILABLE_FORMATS_YCrCb_420_SP:
+                    msg = "YCrCb_420_SP";
+                    ret = 0;
+                    break;
+                case ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED:
+                    msg = "IMPLEMENTATION_DEFINED";
+                    ret = 0;
+                    break;
+                case ANDROID_SCALER_AVAILABLE_FORMATS_YCbCr_420_888:
+                    msg = "YCbCr_420_888";
+                    ret = 0;
+                    break;
+                case ANDROID_SCALER_AVAILABLE_FORMATS_BLOB:
+                    msg = "BLOB";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_JPEG_MIN_DURATIONS: {
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_JPEG_SIZES: {
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM: {
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS: {
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES: {
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_RAW_MIN_DURATIONS: {
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_RAW_SIZES: {
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP: {
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS: {
+            switch (value) {
+                case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT:
+                    msg = "OUTPUT";
+                    ret = 0;
+                    break;
+                case ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT:
+                    msg = "INPUT";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS: {
+            break;
+        }
+        case ANDROID_SCALER_AVAILABLE_STALL_DURATIONS: {
+            break;
+        }
+        case ANDROID_SCALER_CROPPING_TYPE: {
+            switch (value) {
+                case ANDROID_SCALER_CROPPING_TYPE_CENTER_ONLY:
+                    msg = "CENTER_ONLY";
+                    ret = 0;
+                    break;
+                case ANDROID_SCALER_CROPPING_TYPE_FREEFORM:
+                    msg = "FREEFORM";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_SENSOR_EXPOSURE_TIME: {
+            break;
+        }
+        case ANDROID_SENSOR_FRAME_DURATION: {
+            break;
+        }
+        case ANDROID_SENSOR_SENSITIVITY: {
+            break;
+        }
+        case ANDROID_SENSOR_REFERENCE_ILLUMINANT1: {
+            switch (value) {
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT:
+                    msg = "DAYLIGHT";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_FLUORESCENT:
+                    msg = "FLUORESCENT";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_TUNGSTEN:
+                    msg = "TUNGSTEN";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_FLASH:
+                    msg = "FLASH";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_FINE_WEATHER:
+                    msg = "FINE_WEATHER";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_CLOUDY_WEATHER:
+                    msg = "CLOUDY_WEATHER";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_SHADE:
+                    msg = "SHADE";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT_FLUORESCENT:
+                    msg = "DAYLIGHT_FLUORESCENT";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_DAY_WHITE_FLUORESCENT:
+                    msg = "DAY_WHITE_FLUORESCENT";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_COOL_WHITE_FLUORESCENT:
+                    msg = "COOL_WHITE_FLUORESCENT";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_WHITE_FLUORESCENT:
+                    msg = "WHITE_FLUORESCENT";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_STANDARD_A:
+                    msg = "STANDARD_A";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_STANDARD_B:
+                    msg = "STANDARD_B";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_STANDARD_C:
+                    msg = "STANDARD_C";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_D55:
+                    msg = "D55";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_D65:
+                    msg = "D65";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_D75:
+                    msg = "D75";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_D50:
+                    msg = "D50";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN:
+                    msg = "ISO_STUDIO_TUNGSTEN";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_SENSOR_REFERENCE_ILLUMINANT2: {
+            break;
+        }
+        case ANDROID_SENSOR_CALIBRATION_TRANSFORM1: {
+            break;
+        }
+        case ANDROID_SENSOR_CALIBRATION_TRANSFORM2: {
+            break;
+        }
+        case ANDROID_SENSOR_COLOR_TRANSFORM1: {
+            break;
+        }
+        case ANDROID_SENSOR_COLOR_TRANSFORM2: {
+            break;
+        }
+        case ANDROID_SENSOR_FORWARD_MATRIX1: {
+            break;
+        }
+        case ANDROID_SENSOR_FORWARD_MATRIX2: {
+            break;
+        }
+        case ANDROID_SENSOR_BASE_GAIN_FACTOR: {
+            break;
+        }
+        case ANDROID_SENSOR_BLACK_LEVEL_PATTERN: {
+            break;
+        }
+        case ANDROID_SENSOR_MAX_ANALOG_SENSITIVITY: {
+            break;
+        }
+        case ANDROID_SENSOR_ORIENTATION: {
+            break;
+        }
+        case ANDROID_SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS: {
+            break;
+        }
+        case ANDROID_SENSOR_TIMESTAMP: {
+            break;
+        }
+        case ANDROID_SENSOR_TEMPERATURE: {
+            break;
+        }
+        case ANDROID_SENSOR_NEUTRAL_COLOR_POINT: {
+            break;
+        }
+        case ANDROID_SENSOR_NOISE_PROFILE: {
+            break;
+        }
+        case ANDROID_SENSOR_PROFILE_HUE_SAT_MAP: {
+            break;
+        }
+        case ANDROID_SENSOR_PROFILE_TONE_CURVE: {
+            break;
+        }
+        case ANDROID_SENSOR_GREEN_SPLIT: {
+            break;
+        }
+        case ANDROID_SENSOR_TEST_PATTERN_DATA: {
+            break;
+        }
+        case ANDROID_SENSOR_TEST_PATTERN_MODE: {
+            switch (value) {
+                case ANDROID_SENSOR_TEST_PATTERN_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR:
+                    msg = "SOLID_COLOR";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS:
+                    msg = "COLOR_BARS";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_TEST_PATTERN_MODE_COLOR_BARS_FADE_TO_GRAY:
+                    msg = "COLOR_BARS_FADE_TO_GRAY";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_TEST_PATTERN_MODE_PN9:
+                    msg = "PN9";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_TEST_PATTERN_MODE_CUSTOM1:
+                    msg = "CUSTOM1";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES: {
+            break;
+        }
+        case ANDROID_SENSOR_ROLLING_SHUTTER_SKEW: {
+            break;
+        }
+        case ANDROID_SENSOR_OPTICAL_BLACK_REGIONS: {
+            break;
+        }
+        case ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL: {
+            break;
+        }
+        case ANDROID_SENSOR_DYNAMIC_WHITE_LEVEL: {
+            break;
+        }
+        case ANDROID_SENSOR_OPAQUE_RAW_SIZE: {
+            break;
+        }
+
+        case ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE: {
+            break;
+        }
+        case ANDROID_SENSOR_INFO_SENSITIVITY_RANGE: {
+            break;
+        }
+        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT: {
+            switch (value) {
+                case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB:
+                    msg = "RGGB";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG:
+                    msg = "GRBG";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG:
+                    msg = "GBRG";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR:
+                    msg = "BGGR";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB:
+                    msg = "RGB";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_SENSOR_INFO_EXPOSURE_TIME_RANGE: {
+            break;
+        }
+        case ANDROID_SENSOR_INFO_MAX_FRAME_DURATION: {
+            break;
+        }
+        case ANDROID_SENSOR_INFO_PHYSICAL_SIZE: {
+            break;
+        }
+        case ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE: {
+            break;
+        }
+        case ANDROID_SENSOR_INFO_WHITE_LEVEL: {
+            break;
+        }
+        case ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE: {
+            switch (value) {
+                case ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN:
+                    msg = "UNKNOWN";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME:
+                    msg = "REALTIME";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_SENSOR_INFO_LENS_SHADING_APPLIED: {
+            switch (value) {
+                case ANDROID_SENSOR_INFO_LENS_SHADING_APPLIED_FALSE:
+                    msg = "FALSE";
+                    ret = 0;
+                    break;
+                case ANDROID_SENSOR_INFO_LENS_SHADING_APPLIED_TRUE:
+                    msg = "TRUE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE: {
+            break;
+        }
+
+        case ANDROID_SHADING_MODE: {
+            switch (value) {
+                case ANDROID_SHADING_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_SHADING_MODE_FAST:
+                    msg = "FAST";
+                    ret = 0;
+                    break;
+                case ANDROID_SHADING_MODE_HIGH_QUALITY:
+                    msg = "HIGH_QUALITY";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_SHADING_STRENGTH: {
+            break;
+        }
+        case ANDROID_SHADING_AVAILABLE_MODES: {
+            break;
+        }
+
+        case ANDROID_STATISTICS_FACE_DETECT_MODE: {
+            switch (value) {
+                case ANDROID_STATISTICS_FACE_DETECT_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE:
+                    msg = "SIMPLE";
+                    ret = 0;
+                    break;
+                case ANDROID_STATISTICS_FACE_DETECT_MODE_FULL:
+                    msg = "FULL";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_STATISTICS_HISTOGRAM_MODE: {
+            switch (value) {
+                case ANDROID_STATISTICS_HISTOGRAM_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_STATISTICS_HISTOGRAM_MODE_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_STATISTICS_SHARPNESS_MAP_MODE: {
+            switch (value) {
+                case ANDROID_STATISTICS_SHARPNESS_MAP_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_STATISTICS_SHARPNESS_MAP_MODE_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE: {
+            switch (value) {
+                case ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_STATISTICS_HOT_PIXEL_MAP_MODE_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_STATISTICS_FACE_IDS: {
+            break;
+        }
+        case ANDROID_STATISTICS_FACE_LANDMARKS: {
+            break;
+        }
+        case ANDROID_STATISTICS_FACE_RECTANGLES: {
+            break;
+        }
+        case ANDROID_STATISTICS_FACE_SCORES: {
+            break;
+        }
+        case ANDROID_STATISTICS_HISTOGRAM: {
+            break;
+        }
+        case ANDROID_STATISTICS_SHARPNESS_MAP: {
+            break;
+        }
+        case ANDROID_STATISTICS_LENS_SHADING_CORRECTION_MAP: {
+            break;
+        }
+        case ANDROID_STATISTICS_LENS_SHADING_MAP: {
+            break;
+        }
+        case ANDROID_STATISTICS_PREDICTED_COLOR_GAINS: {
+            break;
+        }
+        case ANDROID_STATISTICS_PREDICTED_COLOR_TRANSFORM: {
+            break;
+        }
+        case ANDROID_STATISTICS_SCENE_FLICKER: {
+            switch (value) {
+                case ANDROID_STATISTICS_SCENE_FLICKER_NONE:
+                    msg = "NONE";
+                    ret = 0;
+                    break;
+                case ANDROID_STATISTICS_SCENE_FLICKER_50HZ:
+                    msg = "50HZ";
+                    ret = 0;
+                    break;
+                case ANDROID_STATISTICS_SCENE_FLICKER_60HZ:
+                    msg = "60HZ";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_STATISTICS_HOT_PIXEL_MAP: {
+            break;
+        }
+        case ANDROID_STATISTICS_LENS_SHADING_MAP_MODE: {
+            switch (value) {
+                case ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_STATISTICS_OIS_DATA_MODE: {
+            switch (value) {
+                case ANDROID_STATISTICS_OIS_DATA_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_STATISTICS_OIS_DATA_MODE_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_STATISTICS_OIS_TIMESTAMPS: {
+            break;
+        }
+        case ANDROID_STATISTICS_OIS_X_SHIFTS: {
+            break;
+        }
+        case ANDROID_STATISTICS_OIS_Y_SHIFTS: {
+            break;
+        }
+
+        case ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES: {
+            break;
+        }
+        case ANDROID_STATISTICS_INFO_HISTOGRAM_BUCKET_COUNT: {
+            break;
+        }
+        case ANDROID_STATISTICS_INFO_MAX_FACE_COUNT: {
+            break;
+        }
+        case ANDROID_STATISTICS_INFO_MAX_HISTOGRAM_COUNT: {
+            break;
+        }
+        case ANDROID_STATISTICS_INFO_MAX_SHARPNESS_MAP_VALUE: {
+            break;
+        }
+        case ANDROID_STATISTICS_INFO_SHARPNESS_MAP_SIZE: {
+            break;
+        }
+        case ANDROID_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES: {
+            break;
+        }
+        case ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES: {
+            break;
+        }
+        case ANDROID_STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES: {
+            break;
+        }
+
+        case ANDROID_TONEMAP_CURVE_BLUE: {
+            break;
+        }
+        case ANDROID_TONEMAP_CURVE_GREEN: {
+            break;
+        }
+        case ANDROID_TONEMAP_CURVE_RED: {
+            break;
+        }
+        case ANDROID_TONEMAP_MODE: {
+            switch (value) {
+                case ANDROID_TONEMAP_MODE_CONTRAST_CURVE:
+                    msg = "CONTRAST_CURVE";
+                    ret = 0;
+                    break;
+                case ANDROID_TONEMAP_MODE_FAST:
+                    msg = "FAST";
+                    ret = 0;
+                    break;
+                case ANDROID_TONEMAP_MODE_HIGH_QUALITY:
+                    msg = "HIGH_QUALITY";
+                    ret = 0;
+                    break;
+                case ANDROID_TONEMAP_MODE_GAMMA_VALUE:
+                    msg = "GAMMA_VALUE";
+                    ret = 0;
+                    break;
+                case ANDROID_TONEMAP_MODE_PRESET_CURVE:
+                    msg = "PRESET_CURVE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_TONEMAP_MAX_CURVE_POINTS: {
+            break;
+        }
+        case ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES: {
+            break;
+        }
+        case ANDROID_TONEMAP_GAMMA: {
+            break;
+        }
+        case ANDROID_TONEMAP_PRESET_CURVE: {
+            switch (value) {
+                case ANDROID_TONEMAP_PRESET_CURVE_SRGB:
+                    msg = "SRGB";
+                    ret = 0;
+                    break;
+                case ANDROID_TONEMAP_PRESET_CURVE_REC709:
+                    msg = "REC709";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_LED_TRANSMIT: {
+            switch (value) {
+                case ANDROID_LED_TRANSMIT_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_LED_TRANSMIT_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_LED_AVAILABLE_LEDS: {
+            switch (value) {
+                case ANDROID_LED_AVAILABLE_LEDS_TRANSMIT:
+                    msg = "TRANSMIT";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL: {
+            switch (value) {
+                case ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED:
+                    msg = "LIMITED";
+                    ret = 0;
+                    break;
+                case ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL:
+                    msg = "FULL";
+                    ret = 0;
+                    break;
+                case ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY:
+                    msg = "LEGACY";
+                    ret = 0;
+                    break;
+                case ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_3:
+                    msg = "3";
+                    ret = 0;
+                    break;
+                case ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL:
+                    msg = "EXTERNAL";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_INFO_VERSION: {
+            break;
+        }
+
+        case ANDROID_BLACK_LEVEL_LOCK: {
+            switch (value) {
+                case ANDROID_BLACK_LEVEL_LOCK_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_BLACK_LEVEL_LOCK_ON:
+                    msg = "ON";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_SYNC_FRAME_NUMBER: {
+            switch (value) {
+                case ANDROID_SYNC_FRAME_NUMBER_CONVERGING:
+                    msg = "CONVERGING";
+                    ret = 0;
+                    break;
+                case ANDROID_SYNC_FRAME_NUMBER_UNKNOWN:
+                    msg = "UNKNOWN";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_SYNC_MAX_LATENCY: {
+            switch (value) {
+                case ANDROID_SYNC_MAX_LATENCY_PER_FRAME_CONTROL:
+                    msg = "PER_FRAME_CONTROL";
+                    ret = 0;
+                    break;
+                case ANDROID_SYNC_MAX_LATENCY_UNKNOWN:
+                    msg = "UNKNOWN";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_REPROCESS_EFFECTIVE_EXPOSURE_FACTOR: {
+            break;
+        }
+        case ANDROID_REPROCESS_MAX_CAPTURE_STALL: {
+            break;
+        }
+
+        case ANDROID_DEPTH_MAX_DEPTH_SAMPLES: {
+            break;
+        }
+        case ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS: {
+            switch (value) {
+                case ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_OUTPUT:
+                    msg = "OUTPUT";
+                    ret = 0;
+                    break;
+                case ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_INPUT:
+                    msg = "INPUT";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS: {
+            break;
+        }
+        case ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS: {
+            break;
+        }
+        case ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE: {
+            switch (value) {
+                case ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE_FALSE:
+                    msg = "FALSE";
+                    ret = 0;
+                    break;
+                case ANDROID_DEPTH_DEPTH_IS_EXCLUSIVE_TRUE:
+                    msg = "TRUE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS: {
+            break;
+        }
+        case ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE: {
+            switch (value) {
+                case ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE:
+                    msg = "APPROXIMATE";
+                    ret = 0;
+                    break;
+                case ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED:
+                    msg = "CALIBRATED";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+
+        case ANDROID_DISTORTION_CORRECTION_MODE: {
+            switch (value) {
+                case ANDROID_DISTORTION_CORRECTION_MODE_OFF:
+                    msg = "OFF";
+                    ret = 0;
+                    break;
+                case ANDROID_DISTORTION_CORRECTION_MODE_FAST:
+                    msg = "FAST";
+                    ret = 0;
+                    break;
+                case ANDROID_DISTORTION_CORRECTION_MODE_HIGH_QUALITY:
+                    msg = "HIGH_QUALITY";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
+        case ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES: {
+            break;
+        }
+
+    }
+
+    strncpy(dst, msg, size - 1);
+    dst[size - 1] = '\0';
+
+    return ret;
+}
+
+
+#define CAMERA_METADATA_ENUM_STRING_MAX_SIZE 29