camera.vsoc.jpeg doesn't use VNDK libraries.

camera.vsoc.jpeg was using the following two VNDK libs:
- libandroid_runtime
- libhwui

This CL:
- removes ImageMetadata
- adds Exif/Thumbnail/JpegStub/Compressor from the current
  goldfish implementation

Bug: 71707530
Test: taking a photo works
Change-Id: Ib23613f242c750d8d2bbac28d04ff01da94d4325
(cherry picked from commit d6c77d61e1ac68611175b5c783835d103f8bd97b)
diff --git a/guest/hals/camera/Android.mk b/guest/hals/camera/Android.mk
index 705c933..1f690e7 100644
--- a/guest/hals/camera/Android.mk
+++ b/guest/hals/camera/Android.mk
@@ -28,6 +28,7 @@
 emulator_camera_shared_libraries := \
     libbase \
     libbinder \
+    libexif \
     liblog \
     libutils \
     libcutils \
@@ -49,6 +50,10 @@
 emulator_camera_static_libraries += libjsoncpp
 endif
 
+emulator_camera_static_libraries += \
+    android.hardware.camera.common@1.0-helper \
+    libyuv
+
 emulator_camera_c_includes := \
     device/google/cuttlefish_common \
     frameworks/native/include/media/hardware \
@@ -63,6 +68,8 @@
 		EmulatedCameraDevice.cpp \
 		EmulatedFakeCamera.cpp \
 		EmulatedFakeCameraDevice.cpp \
+		Exif.cpp \
+		Thumbnail.cpp \
 		Converters.cpp \
 		PreviewWindow.cpp \
 		CallbackNotifier.cpp \
@@ -86,7 +93,7 @@
 enable_emulated_camera3 = $(shell test $(PLATFORM_SDK_VERSION) -ge 23 && echo yes)
 enable_emulated_camera2 = $(shell test $(PLATFORM_SDK_VERSION) -ge 19 && echo yes)
 
-# Emulated camera - goldfish / vbox_x86 build###################################
+# Emulated camera - cuttlefish / vbox_x86 build###################################
 #
 ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?))
 emulator_camera_c_includes += external/libjpeg-turbo
@@ -128,56 +135,38 @@
     -Wall \
     -Werror
 
-ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -lt 27 ; echo $$?))
-GCE_HWUI_LIB:=libskia
-GCE_HWUI_INCLUDE:=external/skia/include/core
-else ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -eq 27 -a $(PLATFORM_PREVIEW_SDK_VERSION) -eq 0; echo $$?))
-GCE_HWUI_LIB:=libskia
-GCE_HWUI_INCLUDE:=external/skia/include/core
-else
-GCE_HWUI_LIB:=libhwui
-GCE_HWUI_INCLUDE:=
-endif
-
 jpeg_shared_libraries := \
     libcutils \
+    libexif \
     liblog \
-    $(GCE_HWUI_LIB) \
     libjpeg \
-    libandroid_runtime \
-    cuttlefish_auto_resources
-jpeg_static_libraries := libyuv_static
-jpeg_c_includes := \
-    device/google/cuttlefish_common \
-    external/libyuv/files/include \
-    $(GCE_HWUI_INCLUDE) \
-    frameworks/base/core/jni/android/graphics \
-    frameworks/native/include
-jpeg_src := \
-    JpegStub.cpp \
-    ExifMetadataBuilder.cpp
 
-# JPEG stub - goldfish build####################################################
-
+jpeg_c_includes := external/libexif \
+                   frameworks/native/include
 ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?))
 jpeg_c_includes += external/libjpeg-turbo
 else
 jpeg_c_includes += external/jpeg
 endif
 
-ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
-LOCAL_MODULE_RELATIVE_PATH := ${emulator_camera_module_relative_path}
-else
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/${emulator_camera_module_relative_path}
-endif
+jpeg_src := \
+    Compressor.cpp \
+    JpegStub.cpp \
+
+# JPEG stub - cuttlefish build####################################################
+
 ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
 LOCAL_MULTILIB := first
 endif
+
+LOCAL_C_INCLUDES += ${jpeg_c_includes}
+LOCAL_SRC_FILES := ${jpeg_src}
+
+LOCAL_MODULE_RELATIVE_PATH := ${jpeg_module_relative_path}
 LOCAL_CFLAGS += ${jpeg_cflags}
-LOCAL_CLANG_CFLAGS += ${jpeg_clangflags}
 
 LOCAL_SHARED_LIBRARIES := ${jpeg_shared_libraries}
-LOCAL_STATIC_LIBRARIES := ${jpeg_static_libraries}
+
 LOCAL_C_INCLUDES += ${jpeg_c_includes}
 LOCAL_SRC_FILES := ${jpeg_src}
 
@@ -186,4 +175,3 @@
 LOCAL_VENDOR_MODULE := true
 
 include $(BUILD_SHARED_LIBRARY)
-
diff --git a/guest/hals/camera/CallbackNotifier.cpp b/guest/hals/camera/CallbackNotifier.cpp
index 0c98936..dc67773 100644
--- a/guest/hals/camera/CallbackNotifier.cpp
+++ b/guest/hals/camera/CallbackNotifier.cpp
@@ -25,8 +25,9 @@
 #include <MetadataBufferType.h>
 #include <cutils/log.h>
 #include "EmulatedCameraDevice.h"
-#include "ImageMetadata.h"
 #include "JpegCompressor.h"
+#include "Exif.h"
+#include "Thumbnail.h"
 
 namespace android {
 
@@ -237,27 +238,49 @@
       /* Compress the frame to JPEG. Note that when taking pictures, we
        * have requested camera device to provide us with NV21 frames. */
       NV21JpegCompressor compressor;
-      struct ::ImageMetadata meta;
-      status_t res = camera_dev->getImageMetadata(&meta);
-      if (res == NO_ERROR) {
-        res = compressor.compressRawImage(frame, &meta, mJpegQuality);
-        if (res == NO_ERROR) {
-          camera_memory_t* jpeg_buff =
-              mGetMemoryCB(-1, compressor.getCompressedSize(), 1, mCBOpaque);
-          if (NULL != jpeg_buff && NULL != jpeg_buff->data) {
-            compressor.getCompressedImage(jpeg_buff->data);
-            mDataCB(CAMERA_MSG_COMPRESSED_IMAGE, jpeg_buff, 0, NULL, mCBOpaque);
-            jpeg_buff->release(jpeg_buff);
-          } else {
-            ALOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
+      const CameraParameters* cameraParameters = camera_dev->getCameraParameters();
+      if (cameraParameters == nullptr) {
+        ALOGE("%s: Could not get camera parameters to take picture.", __FUNCTION__);
+        return;
+      }
+
+      ExifData* exifData = createExifData(*cameraParameters);
+
+      // Create a thumbnail and place the pointer and size in the EXIF
+      // data structure. This transfers ownership to the EXIF data and
+      // the memory will be deallocated in the freeExifData call below.
+      int width = camera_dev->getFrameWidth();
+      int height = camera_dev->getFrameHeight();
+      int thumbWidth = cameraParameters->getInt(
+              CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH);
+      int thumbHeight = cameraParameters->getInt(
+              CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT);
+      if (thumbWidth > 0 && thumbHeight > 0) {
+          if (!createThumbnail(static_cast<const unsigned char*>(frame),
+                               width, height, thumbWidth, thumbHeight,
+                               mJpegQuality, exifData)) {
+              // Not really a fatal error, we'll just keep going
+              ALOGE("%s: Failed to create thumbnail for image",
+                    __FUNCTION__);
           }
+      }
+
+      status_t res = compressor.compressRawImage(frame, exifData, mJpegQuality, width, height);
+      if (res == NO_ERROR) {
+        camera_memory_t* jpeg_buff =
+            mGetMemoryCB(-1, compressor.getCompressedSize(), 1, mCBOpaque);
+        if (NULL != jpeg_buff && NULL != jpeg_buff->data) {
+          compressor.getCompressedImage(jpeg_buff->data);
+          mDataCB(CAMERA_MSG_COMPRESSED_IMAGE, jpeg_buff, 0, NULL, mCBOpaque);
+          jpeg_buff->release(jpeg_buff);
         } else {
-          ALOGE("%s: Compression failure in CAMERA_MSG_VIDEO_FRAME",
-                __FUNCTION__);
+          ALOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
         }
       } else {
-        ALOGE("%s: Image Metadata acquisition failure.", __FUNCTION__);
+        ALOGE("%s: Compression failure in CAMERA_MSG_VIDEO_FRAME",
+              __FUNCTION__);
       }
+      freeExifData(exifData);
     }
   }
 }
diff --git a/guest/hals/camera/Compressor.cpp b/guest/hals/camera/Compressor.cpp
new file mode 100644
index 0000000..76c4a24
--- /dev/null
+++ b/guest/hals/camera/Compressor.cpp
@@ -0,0 +1,234 @@
+/*
+* Copyright (C) 2016 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.
+ */
+
+#include "Compressor.h"
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_JPEGStub_Compressor"
+#include <cutils/log.h>
+#include <libexif/exif-data.h>
+
+Compressor::Compressor() {
+
+}
+
+bool Compressor::compress(const unsigned char* data,
+                          int width, int height, int quality,
+                          ExifData* exifData) {
+    if (!configureCompressor(width, height, quality)) {
+        // The method will have logged a more detailed error message than we can
+        // provide here so just return.
+        return false;
+    }
+
+    return compressData(data, exifData);
+}
+
+const std::vector<uint8_t>& Compressor::getCompressedData() const {
+    return mDestManager.mBuffer;
+}
+
+bool Compressor::configureCompressor(int width, int height, int quality) {
+    mCompressInfo.err = jpeg_std_error(&mErrorManager);
+    // NOTE! DANGER! Do not construct any non-trivial objects below setjmp!
+    // The compiler will not generate code to destroy them during the return
+    // below so they will leak. Additionally, do not place any calls to libjpeg
+    // that can fail above this line or any error will cause undefined behavior.
+    if (setjmp(mErrorManager.mJumpBuffer)) {
+        // This is where the error handler will jump in case setup fails
+        // The error manager will ALOG an appropriate error message
+        return false;
+    }
+
+    jpeg_create_compress(&mCompressInfo);
+
+    mCompressInfo.image_width = width;
+    mCompressInfo.image_height = height;
+    mCompressInfo.input_components = 3;
+    mCompressInfo.in_color_space = JCS_YCbCr;
+    jpeg_set_defaults(&mCompressInfo);
+
+    jpeg_set_quality(&mCompressInfo, quality, TRUE);
+    // It may seem weird to set color space here again but this will also set
+    // other fields. These fields might be overwritten by jpeg_set_defaults
+    jpeg_set_colorspace(&mCompressInfo, JCS_YCbCr);
+    mCompressInfo.raw_data_in = TRUE;
+    mCompressInfo.dct_method = JDCT_IFAST;
+    // Set sampling factors
+    mCompressInfo.comp_info[0].h_samp_factor = 2;
+    mCompressInfo.comp_info[0].v_samp_factor = 2;
+    mCompressInfo.comp_info[1].h_samp_factor = 1;
+    mCompressInfo.comp_info[1].v_samp_factor = 1;
+    mCompressInfo.comp_info[2].h_samp_factor = 1;
+    mCompressInfo.comp_info[2].v_samp_factor = 1;
+
+    mCompressInfo.dest = &mDestManager;
+
+    return true;
+}
+
+static void deinterleave(const uint8_t* vuPlanar, std::vector<uint8_t>& uRows,
+                         std::vector<uint8_t>& vRows, int rowIndex, int width,
+                         int height, int stride) {
+    int numRows = (height - rowIndex) / 2;
+    if (numRows > 8) numRows = 8;
+    for (int row = 0; row < numRows; ++row) {
+        int offset = ((rowIndex >> 1) + row) * stride;
+        const uint8_t* vu = vuPlanar + offset;
+        for (int i = 0; i < (width >> 1); ++i) {
+            int index = row * (width >> 1) + i;
+            uRows[index] = vu[1];
+            vRows[index] = vu[0];
+            vu += 2;
+        }
+    }
+}
+
+
+bool Compressor::compressData(const unsigned char* data, ExifData* exifData) {
+    const uint8_t* y[16];
+    const uint8_t* cb[8];
+    const uint8_t* cr[8];
+    const uint8_t** planes[3] = { y, cb, cr };
+
+    int i, offset;
+    int width = mCompressInfo.image_width;
+    int height = mCompressInfo.image_height;
+    const uint8_t* yPlanar = data;
+    const uint8_t* vuPlanar = data + (width * height);
+    std::vector<uint8_t> uRows(8 * (width >> 1));
+    std::vector<uint8_t> vRows(8 * (width >> 1));
+
+    // NOTE! DANGER! Do not construct any non-trivial objects below setjmp!
+    // The compiler will not generate code to destroy them during the return
+    // below so they will leak. Additionally, do not place any calls to libjpeg
+    // that can fail above this line or any error will cause undefined behavior.
+    if (setjmp(mErrorManager.mJumpBuffer)) {
+        // This is where the error handler will jump in case compression fails
+        // The error manager will ALOG an appropriate error message
+        return false;
+    }
+
+    jpeg_start_compress(&mCompressInfo, TRUE);
+
+    attachExifData(exifData);
+
+    // process 16 lines of Y and 8 lines of U/V each time.
+    while (mCompressInfo.next_scanline < mCompressInfo.image_height) {
+        //deinterleave u and v
+        deinterleave(vuPlanar, uRows, vRows, mCompressInfo.next_scanline,
+                     width, height, width);
+
+        // Jpeg library ignores the rows whose indices are greater than height.
+        for (i = 0; i < 16; i++) {
+            // y row
+            y[i] = yPlanar + (mCompressInfo.next_scanline + i) * width;
+
+            // construct u row and v row
+            if ((i & 1) == 0) {
+                // height and width are both halved because of downsampling
+                offset = (i >> 1) * (width >> 1);
+                cb[i/2] = &uRows[offset];
+                cr[i/2] = &vRows[offset];
+            }
+          }
+        jpeg_write_raw_data(&mCompressInfo, const_cast<JSAMPIMAGE>(planes), 16);
+    }
+
+    jpeg_finish_compress(&mCompressInfo);
+    jpeg_destroy_compress(&mCompressInfo);
+
+    return true;
+}
+
+bool Compressor::attachExifData(ExifData* exifData) {
+    if (exifData == nullptr) {
+        // This is not an error, we don't require EXIF data
+        return true;
+    }
+
+    // Save the EXIF data to memory
+    unsigned char* rawData = nullptr;
+    unsigned int size = 0;
+    exif_data_save_data(exifData, &rawData, &size);
+    if (rawData == nullptr) {
+        ALOGE("Failed to create EXIF data block");
+        return false;
+    }
+
+    jpeg_write_marker(&mCompressInfo, JPEG_APP0 + 1, rawData, size);
+    free(rawData);
+    return true;
+}
+
+Compressor::ErrorManager::ErrorManager() {
+    error_exit = &onJpegError;
+}
+
+void Compressor::ErrorManager::onJpegError(j_common_ptr cinfo) {
+    // NOTE! Do not construct any non-trivial objects in this method at the top
+    // scope. Their destructors will not be called. If you do need such an
+    // object create a local scope that does not include the longjmp call,
+    // that ensures the object is destroyed before longjmp is called.
+    ErrorManager* errorManager = reinterpret_cast<ErrorManager*>(cinfo->err);
+
+    // Format and log error message
+    char errorMessage[JMSG_LENGTH_MAX];
+    (*errorManager->format_message)(cinfo, errorMessage);
+    errorMessage[sizeof(errorMessage) - 1] = '\0';
+    ALOGE("JPEG compression error: %s", errorMessage);
+    jpeg_destroy(cinfo);
+
+    // And through the looking glass we go
+    longjmp(errorManager->mJumpBuffer, 1);
+}
+
+Compressor::DestinationManager::DestinationManager() {
+    init_destination = &initDestination;
+    empty_output_buffer = &emptyOutputBuffer;
+    term_destination = &termDestination;
+}
+
+void Compressor::DestinationManager::initDestination(j_compress_ptr cinfo) {
+    auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
+
+    // Start out with some arbitrary but not too large buffer size
+    manager->mBuffer.resize(16 * 1024);
+    manager->next_output_byte = &manager->mBuffer[0];
+    manager->free_in_buffer = manager->mBuffer.size();
+}
+
+boolean Compressor::DestinationManager::emptyOutputBuffer(
+        j_compress_ptr cinfo) {
+    auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
+
+    // Keep doubling the size of the buffer for a very low, amortized
+    // performance cost of the allocations
+    size_t oldSize = manager->mBuffer.size();
+    manager->mBuffer.resize(oldSize * 2);
+    manager->next_output_byte = &manager->mBuffer[oldSize];
+    manager->free_in_buffer = manager->mBuffer.size() - oldSize;
+    return manager->free_in_buffer != 0;
+}
+
+void Compressor::DestinationManager::termDestination(j_compress_ptr cinfo) {
+    auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
+
+    // Resize down to the exact size of the output, that is remove as many
+    // bytes as there are left in the buffer
+    manager->mBuffer.resize(manager->mBuffer.size() - manager->free_in_buffer);
+}
+
diff --git a/guest/hals/camera/Compressor.h b/guest/hals/camera/Compressor.h
new file mode 100644
index 0000000..10f5e80
--- /dev/null
+++ b/guest/hals/camera/Compressor.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef CUTTLEFISH_CAMERA_JPEG_STUB_COMPRESSOR_H
+#define CUTTLEFISH_CAMERA_JPEG_STUB_COMPRESSOR_H
+
+#include <setjmp.h>
+#include <stdlib.h>
+extern "C" {
+#include <jpeglib.h>
+#include <jerror.h>
+}
+
+#include <vector>
+
+struct _ExifData;
+typedef _ExifData ExifData;
+
+class Compressor {
+public:
+    Compressor();
+
+    /* Compress |data| which represents raw NV21 encoded data of dimensions
+     * |width| * |height|. |exifData| is optional EXIF data that will be
+     * attached to the compressed data if present, set to null if not needed.
+     */
+    bool compress(const unsigned char* data,
+                  int width, int height, int quality,
+                  ExifData* exifData);
+
+    /* Get a reference to the compressed data, this will return an empty vector
+     * if compress has not been called yet
+     */
+    const std::vector<unsigned char>& getCompressedData() const;
+
+private:
+    struct DestinationManager : jpeg_destination_mgr {
+        DestinationManager();
+
+        static void initDestination(j_compress_ptr cinfo);
+        static boolean emptyOutputBuffer(j_compress_ptr cinfo);
+        static void termDestination(j_compress_ptr cinfo);
+
+        std::vector<unsigned char> mBuffer;
+    };
+    struct ErrorManager : jpeg_error_mgr {
+        ErrorManager();
+
+        static void onJpegError(j_common_ptr cinfo);
+
+        jmp_buf mJumpBuffer;
+    };
+
+    jpeg_compress_struct mCompressInfo;
+    DestinationManager mDestManager;
+    ErrorManager mErrorManager;
+
+    bool configureCompressor(int width, int height, int quality);
+    bool compressData(const unsigned char* data, ExifData* exifData);
+    bool attachExifData(ExifData* exifData);
+};
+
+#endif  // CUTTLEFISH_CAMERA_JPEG_STUB_COMPRESSOR_H
+
diff --git a/guest/hals/camera/EmulatedBaseCamera.h b/guest/hals/camera/EmulatedBaseCamera.h
index a59ae23..740f334 100644
--- a/guest/hals/camera/EmulatedBaseCamera.h
+++ b/guest/hals/camera/EmulatedBaseCamera.h
@@ -20,8 +20,10 @@
 #include <hardware/camera_common.h>
 #include <utils/Errors.h>
 #include "CameraConfiguration.h"
-#include "ImageMetadata.h"
 #include "guest/libs/platform_support/api_level_fixes.h"
+#include <CameraParameters.h>
+
+using ::android::hardware::camera::common::V1_0::helper::CameraParameters;
 
 namespace android {
 
@@ -96,10 +98,12 @@
    */
   virtual status_t getCameraInfo(struct camera_info* info) = 0;
 
-  /* Gets image metadata.
+  /* Gets camera parameters.
    * This method is called to collect metadata for (currently) taken picture.
    */
-  virtual status_t getImageMetadata(struct ImageMetadata* meta) = 0;
+  virtual const CameraParameters* getCameraParameters() {
+      return NULL;
+  }
 
   /* Set torch mode.
    * This method is called in response to camera_module_t::set_torch_mode
diff --git a/guest/hals/camera/EmulatedCamera.cpp b/guest/hals/camera/EmulatedCamera.cpp
index 923b0b4..24d6158 100644
--- a/guest/hals/camera/EmulatedCamera.cpp
+++ b/guest/hals/camera/EmulatedCamera.cpp
@@ -268,24 +268,8 @@
   return EmulatedBaseCamera::getCameraInfo(info);
 }
 
-status_t EmulatedCamera::getImageMetadata(struct ImageMetadata* meta) {
-  meta->mLensFocalLength =
-      mParameters.getFloat(CameraParameters::KEY_FOCAL_LENGTH);
-  meta->mGpsLatitude = mParameters.getFloat(CameraParameters::KEY_GPS_LATITUDE);
-  meta->mGpsLongitude =
-      mParameters.getFloat(CameraParameters::KEY_GPS_LONGITUDE);
-  meta->mGpsAltitude = mParameters.getFloat(CameraParameters::KEY_GPS_ALTITUDE);
-  meta->mGpsTimestamp = mParameters.getInt(CameraParameters::KEY_GPS_TIMESTAMP);
-  meta->mThumbnailWidth =
-      mParameters.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH);
-  meta->mThumbnailHeight =
-      mParameters.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT);
-  const char* temp =
-      mParameters.get(CameraParameters::KEY_GPS_PROCESSING_METHOD);
-  if (temp) {
-    meta->mGpsProcessingMethod = temp;
-  }
-  return NO_ERROR;
+const CameraParameters* EmulatedCamera::getCameraParameters() {
+  return &mParameters;
 }
 
 status_t EmulatedCamera::setPreviewWindow(struct preview_stream_ops* window) {
diff --git a/guest/hals/camera/EmulatedCamera.h b/guest/hals/camera/EmulatedCamera.h
index 24cd863..9da675d 100644
--- a/guest/hals/camera/EmulatedCamera.h
+++ b/guest/hals/camera/EmulatedCamera.h
@@ -127,7 +127,7 @@
   virtual status_t getCameraInfo(struct camera_info* info);
 
   /** Override of base class method */
-  virtual status_t getImageMetadata(struct ImageMetadata* meta);
+  virtual const CameraParameters* getCameraParameters() override;
 
   /****************************************************************************
    * Camera API implementation.
diff --git a/guest/hals/camera/EmulatedCamera2.h b/guest/hals/camera/EmulatedCamera2.h
index 48b1924..ad8b113 100644
--- a/guest/hals/camera/EmulatedCamera2.h
+++ b/guest/hals/camera/EmulatedCamera2.h
@@ -77,11 +77,6 @@
 
   virtual status_t getCameraInfo(struct camera_info *info) = 0;
 
-  virtual status_t getImageMetadata(struct ImageMetadata * /*meta*/) {
-    // TODO(ender): fill in Image metadata structure.
-    return ENOSYS;
-  }
-
   /****************************************************************************
    * Camera API implementation.
    * These methods are called from the camera API callback routines.
diff --git a/guest/hals/camera/EmulatedCamera3.h b/guest/hals/camera/EmulatedCamera3.h
index e8d2442..92fbb4d 100644
--- a/guest/hals/camera/EmulatedCamera3.h
+++ b/guest/hals/camera/EmulatedCamera3.h
@@ -97,11 +97,6 @@
 
   virtual status_t getCameraInfo(struct camera_info *info);
 
-  virtual status_t getImageMetadata(struct ImageMetadata * /*meta*/) {
-    // TODO(ender): fill in Image metadata structure.
-    return ENOSYS;
-  }
-
   /****************************************************************************
    * Camera API implementation.
    * These methods are called from the camera API callback routines.
diff --git a/guest/hals/camera/EmulatedCameraDevice.cpp b/guest/hals/camera/EmulatedCameraDevice.cpp
index eafbe05..c16f1c8 100644
--- a/guest/hals/camera/EmulatedCameraDevice.cpp
+++ b/guest/hals/camera/EmulatedCameraDevice.cpp
@@ -246,10 +246,8 @@
   }
 }
 
-status_t EmulatedCameraDevice::getImageMetadata(struct ImageMetadata* meta) {
-  meta->mWidth = mFrameWidth;
-  meta->mHeight = mFrameHeight;
-  return mCameraHAL->getImageMetadata(meta);
+const CameraParameters* EmulatedCameraDevice::getCameraParameters() {
+  return mCameraHAL->getCameraParameters();
 }
 
 /****************************************************************************
diff --git a/guest/hals/camera/EmulatedCameraDevice.h b/guest/hals/camera/EmulatedCameraDevice.h
index ccd786f..ab654eb 100644
--- a/guest/hals/camera/EmulatedCameraDevice.h
+++ b/guest/hals/camera/EmulatedCameraDevice.h
@@ -31,7 +31,10 @@
 #include <utils/threads.h>
 #include "Converters.h"
 #include "EmulatedCameraCommon.h"
-#include "ImageMetadata.h"
+
+#include <CameraParameters.h>
+
+using ::android::hardware::camera::common::V1_0::helper::CameraParameters;
 
 namespace android {
 
@@ -260,7 +263,7 @@
    * Return:
    *  Filled in ImageMetadata structure (in/out parameter).
    */
-  status_t getImageMetadata(struct ::ImageMetadata* meta);
+  const CameraParameters* getCameraParameters();
 
   /*
    * State checkers.
diff --git a/guest/hals/camera/Exif.cpp b/guest/hals/camera/Exif.cpp
new file mode 100644
index 0000000..afdcf4e
--- /dev/null
+++ b/guest/hals/camera/Exif.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2016 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_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Exif"
+#include <cutils/log.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <stdint.h>
+
+#include <CameraParameters.h>
+
+using ::android::hardware::camera::common::V1_0::helper::CameraParameters;
+using ::android::hardware::camera::common::V1_0::helper::Size;
+
+#include "Exif.h"
+
+#include <libexif/exif-data.h>
+#include <libexif/exif-entry.h>
+#include <libexif/exif-ifd.h>
+#include <libexif/exif-tag.h>
+
+#include <string>
+#include <vector>
+
+// For GPS timestamping we want to ensure we use a 64-bit time_t, 32-bit
+// platforms have time64_t but 64-bit platforms do not.
+#if defined(__LP64__)
+#include <time.h>
+using Timestamp = time_t;
+#define TIMESTAMP_TO_TM(timestamp, tm) gmtime_r(timestamp, tm)
+#else
+#include <time64.h>
+using Timestamp = time64_t;
+#define TIMESTAMP_TO_TM(timestamp, tm) gmtime64_r(timestamp, tm)
+#endif
+
+namespace android {
+
+// A prefix that is used for tags with the "undefined" format to indicate that
+// the contents are ASCII encoded. See the user comment section of the EXIF spec
+// for more details http://www.exif.org/Exif2-2.PDF
+static const unsigned char kAsciiPrefix[] = {
+    0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00 // "ASCII\0\0\0"
+};
+
+// Remove an existing EXIF entry from |exifData| if it exists. This is useful
+// when replacing existing data, it's easier to just remove the data and
+// re-allocate it than to adjust the amount of allocated data.
+static void removeExistingEntry(ExifData* exifData, ExifIfd ifd, int tag) {
+    ExifEntry* entry = exif_content_get_entry(exifData->ifd[ifd],
+                                              static_cast<ExifTag>(tag));
+    if (entry) {
+        exif_content_remove_entry(exifData->ifd[ifd], entry);
+    }
+}
+
+static ExifEntry* allocateEntry(int tag,
+                                ExifFormat format,
+                                unsigned int numComponents) {
+    ExifMem* mem = exif_mem_new_default();
+    ExifEntry* entry = exif_entry_new_mem(mem);
+
+    unsigned int size = numComponents * exif_format_get_size(format);
+    entry->data = reinterpret_cast<unsigned char*>(exif_mem_alloc(mem, size));
+    entry->size = size;
+    entry->tag = static_cast<ExifTag>(tag);
+    entry->components = numComponents;
+    entry->format = format;
+
+    exif_mem_unref(mem);
+    return entry;
+}
+
+// Create an entry and place it in |exifData|, the entry is initialized with an
+// array of floats from |values|
+template<size_t N>
+static bool createEntry(ExifData* exifData,
+                        ExifIfd ifd,
+                        int tag,
+                        const float (&values)[N],
+                        float denominator = 1000.0) {
+    removeExistingEntry(exifData, ifd, tag);
+    ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
+    ExifEntry* entry = allocateEntry(tag, EXIF_FORMAT_RATIONAL, N);
+    exif_content_add_entry(exifData->ifd[ifd], entry);
+    unsigned int rationalSize = exif_format_get_size(EXIF_FORMAT_RATIONAL);
+    for (size_t i = 0; i < N; ++i) {
+        ExifRational rational = {
+            static_cast<uint32_t>(values[i] * denominator),
+            static_cast<uint32_t>(denominator)
+        };
+
+        exif_set_rational(&entry->data[i * rationalSize], byteOrder, rational);
+    }
+
+    // Unref entry after changing owner to the ExifData struct
+    exif_entry_unref(entry);
+    return true;
+}
+
+// Create an entry with a single float |value| in it and place it in |exifData|
+static bool createEntry(ExifData* exifData,
+                        ExifIfd ifd,
+                        int tag,
+                        const float value,
+                        float denominator = 1000.0) {
+    float values[1] = { value };
+    // Recycling functions is good for the environment
+    return createEntry(exifData, ifd, tag, values, denominator);
+}
+
+// Create an entry and place it in |exifData|, the entry contains the raw data
+// pointed to by |data| of length |size|.
+static bool createEntry(ExifData* exifData,
+                        ExifIfd ifd,
+                        int tag,
+                        const unsigned char* data,
+                        size_t size,
+                        ExifFormat format = EXIF_FORMAT_UNDEFINED) {
+    removeExistingEntry(exifData, ifd, tag);
+    ExifEntry* entry = allocateEntry(tag, format, size);
+    memcpy(entry->data, data, size);
+    exif_content_add_entry(exifData->ifd[ifd], entry);
+    // Unref entry after changing owner to the ExifData struct
+    exif_entry_unref(entry);
+    return true;
+}
+
+// Create an entry and place it in |exifData|, the entry is initialized with
+// the string provided in |value|
+static bool createEntry(ExifData* exifData,
+                        ExifIfd ifd,
+                        int tag,
+                        const char* value) {
+    unsigned int length = strlen(value) + 1;
+    const unsigned char* data = reinterpret_cast<const unsigned char*>(value);
+    return createEntry(exifData, ifd, tag, data, length, EXIF_FORMAT_ASCII);
+}
+
+// Create an entry and place it in |exifData|, the entry is initialized with a
+// single byte in |value|
+static bool createEntry(ExifData* exifData,
+                        ExifIfd ifd,
+                        int tag,
+                        uint8_t value) {
+    return createEntry(exifData, ifd, tag, &value, 1, EXIF_FORMAT_BYTE);
+}
+
+// Create an entry and place it in |exifData|, the entry is default initialized
+// by the exif library based on |tag|
+static bool createEntry(ExifData* exifData,
+                        ExifIfd ifd,
+                        int tag) {
+    removeExistingEntry(exifData, ifd, tag);
+    ExifEntry* entry = exif_entry_new();
+    exif_content_add_entry(exifData->ifd[ifd], entry);
+    exif_entry_initialize(entry, static_cast<ExifTag>(tag));
+    // Unref entry after changing owner to the ExifData struct
+    exif_entry_unref(entry);
+    return true;
+}
+
+// Create an entry with a single EXIF LONG (32-bit value) and place it in
+// |exifData|.
+static bool createEntry(ExifData* exifData,
+                        ExifIfd ifd,
+                        int tag,
+                        int value) {
+    removeExistingEntry(exifData, ifd, tag);
+    ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
+    ExifEntry* entry = allocateEntry(tag, EXIF_FORMAT_LONG, 1);
+    exif_content_add_entry(exifData->ifd[ifd], entry);
+    exif_set_long(entry->data, byteOrder, value);
+
+    // Unref entry after changing owner to the ExifData struct
+    exif_entry_unref(entry);
+    return true;
+}
+
+static bool getCameraParam(const CameraParameters& parameters,
+                           const char* parameterKey,
+                           const char** outValue) {
+    const char* value = parameters.get(parameterKey);
+    if (value) {
+        *outValue = value;
+        return true;
+    }
+    return false;
+}
+
+static bool getCameraParam(const CameraParameters& parameters,
+                           const char* parameterKey,
+                           float* outValue) {
+    const char* value = parameters.get(parameterKey);
+    if (value) {
+        *outValue = parameters.getFloat(parameterKey);
+        return true;
+    }
+    return false;
+}
+
+static bool getCameraParam(const CameraParameters& parameters,
+                           const char* parameterKey,
+                           int64_t* outValue) {
+    const char* value = parameters.get(parameterKey);
+    if (value) {
+        char dummy = 0;
+        // Attempt to scan an extra character and then make sure it was not
+        // scanned by checking that the return value indicates only one item.
+        // This way we fail on any trailing characters
+        if (sscanf(value, "%" SCNd64 "%c", outValue, &dummy) == 1) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// Convert a GPS coordinate represented as a decimal degree value to sexagesimal
+// GPS coordinates comprised of <degrees> <minutes>' <seconds>"
+static void convertGpsCoordinate(float degrees, float (*result)[3]) {
+    float absDegrees = fabs(degrees);
+    // First value is degrees without any decimal digits
+    (*result)[0] = floor(absDegrees);
+
+    // Subtract degrees so we only have the fraction left, then multiply by
+    // 60 to get the minutes
+    float minutes = (absDegrees - (*result)[0]) * 60.0f;
+    (*result)[1] = floor(minutes);
+
+    // Same thing for seconds but here we store seconds with the fraction
+    float seconds = (minutes - (*result)[1]) * 60.0f;
+    (*result)[2] = seconds;
+}
+
+// Convert a UNIX epoch timestamp to a timestamp comprised of three floats for
+// hour, minute and second, and a date part that is represented as a string.
+static bool convertTimestampToTimeAndDate(int64_t timestamp,
+                                          float (*timeValues)[3],
+                                          std::string* date) {
+    Timestamp time = timestamp;
+    struct tm utcTime;
+    if (TIMESTAMP_TO_TM(&time, &utcTime) == nullptr) {
+        ALOGE("Could not decompose timestamp into components");
+        return false;
+    }
+    (*timeValues)[0] = utcTime.tm_hour;
+    (*timeValues)[1] = utcTime.tm_min;
+    (*timeValues)[2] = utcTime.tm_sec;
+
+    char buffer[64] = {};
+    if (strftime(buffer, sizeof(buffer), "%Y:%m:%d", &utcTime) == 0) {
+        ALOGE("Could not construct date string from timestamp");
+        return false;
+    }
+    *date = buffer;
+    return true;
+}
+
+ExifData* createExifData(const CameraParameters& params) {
+    ExifData* exifData = exif_data_new();
+
+    exif_data_set_option(exifData, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
+    exif_data_set_data_type(exifData, EXIF_DATA_TYPE_COMPRESSED);
+    exif_data_set_byte_order(exifData, EXIF_BYTE_ORDER_INTEL);
+
+    // Create mandatory exif fields and set their default values
+    exif_data_fix(exifData);
+
+    float triplet[3];
+    float floatValue = 0.0f;
+    const char* stringValue;
+    int64_t degrees;
+
+    // Datetime, creating and initializing a datetime tag will automatically
+    // set the current date and time in the tag so just do that.
+    createEntry(exifData, EXIF_IFD_0, EXIF_TAG_DATE_TIME);
+
+    // Make and model
+    createEntry(exifData, EXIF_IFD_0, EXIF_TAG_MAKE, "Emulator-Cuttlefish");
+    createEntry(exifData, EXIF_IFD_0, EXIF_TAG_MODEL, "Emulator-Cuttlefish");
+
+    // Picture size
+    int width = -1, height = -1;
+    params.getPictureSize(&width, &height);
+    if (width >= 0 && height >= 0) {
+        createEntry(exifData, EXIF_IFD_EXIF,
+                    EXIF_TAG_PIXEL_X_DIMENSION, width);
+        createEntry(exifData, EXIF_IFD_EXIF,
+                    EXIF_TAG_PIXEL_Y_DIMENSION, height);
+    }
+    // Orientation
+    if (getCameraParam(params,
+                       CameraParameters::KEY_ROTATION,
+                       &degrees)) {
+        // Exif orientation values, please refer to
+        // http://www.exif.org/Exif2-2.PDF, Section 4.6.4-A-Orientation
+        // Or these websites:
+        // http://sylvana.net/jpegcrop/exif_orientation.html
+        // http://www.impulseadventure.com/photo/exif-orientation.html
+        enum {
+            EXIF_ROTATE_CAMERA_CW0 = 1,
+            EXIF_ROTATE_CAMERA_CW90 = 6,
+            EXIF_ROTATE_CAMERA_CW180 = 3,
+            EXIF_ROTATE_CAMERA_CW270 = 8,
+        };
+        uint16_t exifOrien = 1;
+        switch (degrees) {
+            case 0:
+                exifOrien = EXIF_ROTATE_CAMERA_CW0;
+                break;
+            case 90:
+                exifOrien = EXIF_ROTATE_CAMERA_CW90;
+                break;
+            case 180:
+                exifOrien = EXIF_ROTATE_CAMERA_CW180;
+                break;
+            case 270:
+                exifOrien = EXIF_ROTATE_CAMERA_CW270;
+                break;
+        }
+        createEntry(exifData, EXIF_IFD_0, EXIF_TAG_ORIENTATION, exifOrien);
+    }
+    // Focal length
+    if (getCameraParam(params,
+                       CameraParameters::KEY_FOCAL_LENGTH,
+                       &floatValue)) {
+        createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, floatValue);
+    }
+    // GPS latitude and reference, reference indicates sign, store unsigned
+    if (getCameraParam(params,
+                       CameraParameters::KEY_GPS_LATITUDE,
+                       &floatValue)) {
+        convertGpsCoordinate(floatValue, &triplet);
+        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE, triplet);
+
+        const char* ref = floatValue < 0.0f ? "S" : "N";
+        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE_REF, ref);
+    }
+    // GPS longitude and reference, reference indicates sign, store unsigned
+    if (getCameraParam(params,
+                       CameraParameters::KEY_GPS_LONGITUDE,
+                       &floatValue)) {
+        convertGpsCoordinate(floatValue, &triplet);
+        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE, triplet);
+
+        const char* ref = floatValue < 0.0f ? "W" : "E";
+        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE_REF, ref);
+    }
+    // GPS altitude and reference, reference indicates sign, store unsigned
+    if (getCameraParam(params,
+                       CameraParameters::KEY_GPS_ALTITUDE,
+                       &floatValue)) {
+        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE,
+                    static_cast<float>(fabs(floatValue)));
+
+        // 1 indicated below sea level, 0 indicates above sea level
+        uint8_t ref = floatValue < 0.0f ? 1 : 0;
+        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE_REF, ref);
+    }
+    // GPS timestamp and datestamp
+    int64_t timestamp = 0;
+    if (getCameraParam(params,
+                       CameraParameters::KEY_GPS_TIMESTAMP,
+                       &timestamp)) {
+        std::string date;
+        if (convertTimestampToTimeAndDate(timestamp, &triplet, &date)) {
+            createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_TIME_STAMP,
+                        triplet, 1.0f);
+            createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_DATE_STAMP,
+                        date.c_str());
+        }
+    }
+
+    // GPS processing method
+    if (getCameraParam(params,
+                       CameraParameters::KEY_GPS_PROCESSING_METHOD,
+                       &stringValue)) {
+        std::vector<unsigned char> data;
+        // Because this is a tag with an undefined format it has to be prefixed
+        // with the encoding type. Insert an ASCII prefix first, then the
+        // actual string. Undefined tags do not have to be null terminated.
+        data.insert(data.end(),
+                    std::begin(kAsciiPrefix),
+                    std::end(kAsciiPrefix));
+        data.insert(data.end(), stringValue, stringValue + strlen(stringValue));
+        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_PROCESSING_METHOD,
+                    &data[0], data.size());
+    }
+
+    return exifData;
+}
+
+void freeExifData(ExifData* exifData) {
+    exif_data_free(exifData);
+}
+
+}  // namespace android
+
diff --git a/guest/hals/camera/Exif.h b/guest/hals/camera/Exif.h
new file mode 100644
index 0000000..b4f0963
--- /dev/null
+++ b/guest/hals/camera/Exif.h
@@ -0,0 +1,41 @@
+
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef CUTTLEFISH_CAMERA_EXIF_H
+#define CUTTLEFISH_CAMERA_EXIF_H
+
+#include <CameraParameters.h>
+
+struct _ExifData;
+typedef struct _ExifData ExifData;
+
+using ::android::hardware::camera::common::V1_0::helper::CameraParameters;
+
+namespace android {
+
+/* Create an EXIF data structure based on camera parameters. This includes
+ * things like GPS information that has been set by the camera client.
+ */
+ExifData* createExifData(const CameraParameters& parameters);
+
+/* Free EXIF data created in the createExifData call */
+void freeExifData(ExifData* exifData);
+
+}  // namespace android
+
+#endif  // CUTTLEFISH_CAMERA_EXIF_H
+
diff --git a/guest/hals/camera/ExifMetadataBuilder.cpp b/guest/hals/camera/ExifMetadataBuilder.cpp
deleted file mode 100644
index 17cc75b..0000000
--- a/guest/hals/camera/ExifMetadataBuilder.cpp
+++ /dev/null
@@ -1,515 +0,0 @@
-#include "ExifMetadataBuilder.h"
-
-#define LOG_NDEBUG 0
-#define LOG_TAG "ExifMetadataBuilder"
-#include <cutils/log.h>
-
-#include <stdlib.h>
-#include <cmath>
-
-namespace android {
-// All supported EXIF data types.
-enum ExifDataType {
-  ExifUInt8 = 1,
-  ExifString = 2,
-  ExifUInt16 = 3,
-  ExifUInt32 = 4,
-  ExifRational = 5,
-  ExifUndefined = 7,
-  ExifSInt16 = 8,
-  ExifSInt32 = 9,
-  ExifFloat = 11,
-  ExifDouble = 12,
-};
-
-enum ExifTagId {
-  kExifTagGpsLatitudeRef = 0x1,
-  kExifTagGpsLatitude = 0x2,
-  kExifTagGpsLongitudeRef = 0x3,
-  kExifTagGpsLongitude = 0x4,
-  kExifTagGpsAltitudeRef = 0x5,
-  kExifTagGpsAltitude = 0x6,
-  kExifTagGpsTimestamp = 0x7,
-  kExifTagGpsProcessingMethod = 0x1b,
-  kExifTagGpsDatestamp = 0x1d,
-  kExifTagImageWidth = 0x100,
-  kExifTagImageHeight = 0x101,
-  kExifTagImageDateTime = 0x132,
-  kExifTagJpegData = 0x201,
-  kExifTagJpegLength = 0x202,
-  kExifTagCameraSubIFD = 0x8769,
-  kExifTagGpsSubIFD = 0x8825,
-  kExifTagCameraFocalLength = 0x920a,
-};
-
-const char kExifCharArrayAscii[8] = "ASCII";
-// const char kExifCharArrayUnicode[8] = "UNICODE";
-
-// Structure of an individual EXIF tag.
-struct ExifTagInfo {
-  uint16_t tag;    // Describes unique tag type.
-  uint16_t type;   // Describes data type (see ExifDataType).
-  uint32_t count;  // Number of data elements.
-  uint32_t value;  // Value (for shorter items) or offset (for longer).
-};
-
-// Interface describing individual EXIF tag.
-class ExifTag {
- public:
-  virtual ~ExifTag() {}
-  virtual size_t DataSize() { return 0; }
-  virtual void AppendTag(ExifTagInfo* info, size_t data_offset) = 0;
-  virtual void AppendData(uint8_t* /*target*/) {}
-};
-
-// EXIF structure.
-// This structure describes all types of tags:
-// - Main image,
-// - Thumbnail image,
-// - Sub-IFDs.
-class ExifStructure {
- public:
-  typedef std::vector<ExifTag*> TagMap;
-
-  ExifStructure() {}
-
-  ~ExifStructure() {
-    for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
-      delete *it;
-    }
-  }
-
-  size_t TagSize() {
-    // Target tag structure:
-    // - uint16_t: mTags.size();
-    // - ExifTagInfo[mTags.size()]
-    // - uint32_t: next_structure_available ? self_offset + Size() : NULL
-    return sizeof(uint16_t)                      // mTags.size()
-           + mTags.size() * sizeof(ExifTagInfo)  // [tags]
-           + sizeof(uint32_t);                   // next offset
-  }
-
-  size_t DataSize() {
-    size_t data_size = 0;
-    for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
-      data_size += (*it)->DataSize();
-    }
-    return data_size;
-  }
-
-  size_t Size() { return TagSize() + DataSize(); }
-
-  uint32_t Build(uint8_t* buffer, const uint32_t self_offset,
-                 const bool next_structure_available) {
-    // Write number of items.
-    uint16_t num_elements = mTags.size();
-    memcpy(buffer, &num_elements, sizeof(num_elements));
-    buffer += sizeof(num_elements);
-
-    // Offset describes where tag data will be placed.
-    uint32_t offset = self_offset + TagSize();
-
-    // Combine EXIF for main image.
-    for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
-      // Each tag is exactly 12 bytes long, but data length can be anything.
-      // We supply the data offset to anyone who wants to use data.
-      (*it)->AppendTag(reinterpret_cast<ExifTagInfo*>(buffer), offset);
-      offset += (*it)->DataSize();
-      buffer += sizeof(ExifTagInfo);
-    }
-
-    // Append information about the second tag offset.
-    // |offset| holds exactly the right position:
-    // self_offset + TagSize() + DataSize().
-    uint32_t next_tag_offset = next_structure_available ? offset : 0;
-    memcpy(buffer, &next_tag_offset, sizeof(next_tag_offset));
-    buffer += sizeof(next_tag_offset);
-
-    // Combine EXIF data for main image.
-    for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
-      (*it)->AppendData(buffer);
-      buffer += (*it)->DataSize();
-    }
-
-    return offset;
-  }
-
-  void PushTag(ExifTag* tag) { mTags.push_back(tag); }
-
- private:
-  TagMap mTags;
-};
-
-// EXIF tags.
-namespace {
-// Tag with 8-bit unsigned integer.
-class ExifUInt8Tag : public ExifTag {
- public:
-  ExifUInt8Tag(uint16_t tag, size_t value) : mTag(tag), mValue(value) {}
-
-  void AppendTag(ExifTagInfo* info, size_t /*data_offset*/) {
-    info->tag = mTag;
-    info->type = ExifUInt8;
-    info->count = 1;
-    info->value = mValue << 24;
-  }
-
- private:
-  uint16_t mTag;
-  uint32_t mValue;
-};
-
-// Tag with 32-bit unsigned integer.
-class ExifUInt32Tag : public ExifTag {
- public:
-  ExifUInt32Tag(uint16_t tag, size_t value) : mTag(tag), mValue(value) {}
-
-  void AppendTag(ExifTagInfo* info, size_t /*data_offset*/) {
-    info->tag = mTag;
-    info->type = ExifUInt32;
-    info->count = 1;
-    info->value = mValue;
-  }
-
- private:
-  uint16_t mTag;
-  uint32_t mValue;
-};
-
-// Char array tag.
-class ExifCharArrayTag : public ExifTag {
- public:
-  ExifCharArrayTag(uint16_t tag, const char (&type)[8], const std::string& str)
-      : mTag(tag), mType(type), mString(str) {}
-
-  void AppendTag(ExifTagInfo* info, size_t data_offset) {
-    info->tag = mTag;
-    info->type = ExifUndefined;
-    info->count = DataSize();
-    info->value = data_offset;
-  }
-
-  size_t DataSize() { return sizeof(mType) + mString.size(); }
-
-  void AppendData(uint8_t* data) {
-    memcpy(data, mType, sizeof(mType));
-    data += sizeof(mType);
-    memcpy(data, mString.data(), mString.size());
-  }
-
- private:
-  uint16_t mTag;
-  const char (&mType)[8];
-  std::string mString;
-};
-
-// Data tag; writes LONG (pointer) and appends data.
-class ExifPointerTag : public ExifTag {
- public:
-  ExifPointerTag(uint16_t tag, void* data, int size)
-      : mTag(tag), mData(data), mSize(size) {}
-
-  ~ExifPointerTag() { free(mData); }
-
-  void AppendTag(ExifTagInfo* info, size_t data_offset) {
-    info->tag = mTag;
-    info->type = ExifUInt32;
-    info->count = 1;
-    info->value = data_offset;
-  }
-
-  size_t DataSize() { return mSize; }
-
-  void AppendData(uint8_t* data) { memcpy(data, mData, mSize); }
-
- private:
-  uint16_t mTag;
-  void* mData;
-  int mSize;
-};
-
-// String tag.
-class ExifStringTag : public ExifTag {
- public:
-  ExifStringTag(uint16_t tag, const std::string& str)
-      : mTag(tag), mString(str) {}
-
-  void AppendTag(ExifTagInfo* info, size_t data_offset) {
-    info->tag = mTag;
-    info->type = ExifString;
-    info->count = DataSize();
-    info->value = data_offset;
-  }
-
-  size_t DataSize() {
-    // Include padding \0.
-    return mString.size() + 1;
-  }
-
-  void AppendData(uint8_t* data) { memcpy(data, mString.data(), DataSize()); }
-
- private:
-  uint16_t mTag;
-  std::string mString;
-};
-
-// SubIFD: sub-tags.
-class ExifSubIfdTag : public ExifTag {
- public:
-  ExifSubIfdTag(uint16_t tag) : mTag(tag), mSubStructure(new ExifStructure) {}
-
-  ~ExifSubIfdTag() { delete mSubStructure; }
-
-  void AppendTag(ExifTagInfo* info, size_t data_offset) {
-    info->tag = mTag;
-    info->type = ExifUInt32;
-    info->count = 1;
-    info->value = data_offset;
-    mDataOffset = data_offset;
-  }
-
-  size_t DataSize() { return mSubStructure->Size(); }
-
-  void AppendData(uint8_t* data) {
-    mSubStructure->Build(data, mDataOffset, false);
-  }
-
-  ExifStructure* GetSubStructure() { return mSubStructure; }
-
- private:
-  uint16_t mTag;
-  ExifStructure* mSubStructure;
-  size_t mDataOffset;
-};
-
-// Unsigned rational tag.
-class ExifURationalTag : public ExifTag {
- public:
-  ExifURationalTag(uint16_t tag, double value) : mTag(tag), mCount(1) {
-    DoubleToRational(value, &mRationals[0].mNumerator,
-                     &mRationals[0].mDenominator);
-  }
-
-  ExifURationalTag(uint16_t tag, double value1, double value2, double value3)
-      : mTag(tag), mCount(3) {
-    DoubleToRational(value1, &mRationals[0].mNumerator,
-                     &mRationals[0].mDenominator);
-    DoubleToRational(value2, &mRationals[1].mNumerator,
-                     &mRationals[1].mDenominator);
-    DoubleToRational(value3, &mRationals[2].mNumerator,
-                     &mRationals[2].mDenominator);
-  }
-
-  void DoubleToRational(double value, int32_t* numerator,
-                        int32_t* denominator) {
-    int sign = 1;
-    if (value < 0) {
-      sign = -sign;
-      value = -value;
-    }
-    // Take shortcuts. Plenty.
-    *numerator = value;
-    *denominator = 1;
-
-    // Set some (arbitrary) threshold beyond which we do not proceed.
-    while (*numerator < (1 << 24)) {
-      if (std::fabs((*numerator / *denominator) - value) <= 0.0001) break;
-      *denominator *= 10;
-      *numerator = value * (*denominator);
-    }
-    *numerator *= sign;
-  }
-
-  void AppendTag(ExifTagInfo* info, size_t data_offset) {
-    info->tag = mTag;
-    info->type = ExifRational;
-    info->count = mCount;
-    info->value = data_offset;
-  }
-
-  size_t DataSize() { return sizeof(mRationals[0]) * mCount; }
-
-  void AppendData(uint8_t* data) { memcpy(data, &mRationals[0], DataSize()); }
-
- private:
-  static const int kMaxSupportedRationals = 3;
-  uint16_t mTag;
-  uint16_t mCount;
-  struct {
-    int32_t mNumerator;
-    int32_t mDenominator;
-  } mRationals[kMaxSupportedRationals];
-};
-
-std::string ToAsciiDate(time_t time) {
-  struct tm loc;
-  char res[12];  // YYYY:MM:DD\0\0
-  localtime_r(&time, &loc);
-  strftime(res, sizeof(res), "%Y:%m:%d", &loc);
-  return res;
-}
-
-std::string ToAsciiTime(time_t time) {
-  struct tm loc;
-  char res[10];  // HH:MM:SS\0\0
-  localtime_r(&time, &loc);
-  strftime(res, sizeof(res), "%H:%M:%S", &loc);
-  return res;
-}
-
-}  // namespace
-
-ExifMetadataBuilder::ExifMetadataBuilder()
-    : mImageIfd(new ExifStructure), mThumbnailIfd(new ExifStructure) {
-  // Mandatory tag: camera details.
-  ExifSubIfdTag* sub_ifd = new ExifSubIfdTag(kExifTagCameraSubIFD);
-  // Pass ownership to mImageIfd.
-  mImageIfd->PushTag(sub_ifd);
-  mCameraSubIfd = sub_ifd->GetSubStructure();
-
-  // Optional (yet, required) tag: GPS data.
-  sub_ifd = new ExifSubIfdTag(kExifTagGpsSubIFD);
-  // Pass ownership to mImageIfd.
-  mImageIfd->PushTag(sub_ifd);
-  mGpsSubIfd = sub_ifd->GetSubStructure();
-}
-
-ExifMetadataBuilder::~ExifMetadataBuilder() {
-  delete mImageIfd;
-  delete mThumbnailIfd;
-}
-
-void ExifMetadataBuilder::SetWidth(int width) {
-  mImageIfd->PushTag(new ExifUInt32Tag(kExifTagImageWidth, width));
-}
-
-void ExifMetadataBuilder::SetHeight(int height) {
-  mImageIfd->PushTag(new ExifUInt32Tag(kExifTagImageHeight, height));
-}
-
-void ExifMetadataBuilder::SetThumbnailWidth(int width) {
-  mThumbnailIfd->PushTag(new ExifUInt32Tag(kExifTagImageWidth, width));
-}
-
-void ExifMetadataBuilder::SetThumbnailHeight(int height) {
-  mThumbnailIfd->PushTag(new ExifUInt32Tag(kExifTagImageHeight, height));
-}
-
-void ExifMetadataBuilder::SetThumbnail(void* thumbnail, int size) {
-  mThumbnailIfd->PushTag(new ExifUInt32Tag(kExifTagJpegLength, size));
-  mThumbnailIfd->PushTag(new ExifPointerTag(kExifTagJpegData, thumbnail, size));
-}
-
-void ExifMetadataBuilder::SetDateTime(time_t date_time) {
-  std::string res = ToAsciiDate(date_time) + " " + ToAsciiTime(date_time);
-  mImageIfd->PushTag(new ExifStringTag(kExifTagImageDateTime, res));
-}
-
-void ExifMetadataBuilder::SetGpsLatitude(double latitude) {
-  if (latitude < 0) {
-    mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLatitudeRef, "S"));
-  } else {
-    mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLatitudeRef, "N"));
-  }
-  int degrees = latitude;
-  latitude = (latitude - degrees) * 60.;
-  int minutes = latitude;
-  latitude = (latitude - minutes) * 60.;
-  double seconds = latitude;
-  mGpsSubIfd->PushTag(
-      new ExifURationalTag(kExifTagGpsLatitude, degrees, minutes, seconds));
-}
-
-void ExifMetadataBuilder::SetGpsLongitude(double longitude) {
-  if (longitude < 0) {
-    mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLongitudeRef, "W"));
-  } else {
-    mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLongitudeRef, "E"));
-  }
-  int32_t degrees = longitude;
-  longitude = (longitude - degrees) * 60.;
-  int32_t minutes = longitude;
-  longitude = (longitude - minutes) * 60.;
-  double seconds = longitude;
-  mGpsSubIfd->PushTag(
-      new ExifURationalTag(kExifTagGpsLongitude, degrees, minutes, seconds));
-}
-
-void ExifMetadataBuilder::SetGpsAltitude(double altitude) {
-  mGpsSubIfd->PushTag(new ExifUInt8Tag(kExifTagGpsAltitudeRef, 0));
-  mGpsSubIfd->PushTag(new ExifURationalTag(kExifTagGpsAltitude, altitude));
-}
-
-void ExifMetadataBuilder::SetGpsProcessingMethod(const std::string& method) {
-  mGpsSubIfd->PushTag(new ExifCharArrayTag(kExifTagGpsProcessingMethod,
-                                           kExifCharArrayAscii, method));
-}
-
-void ExifMetadataBuilder::SetGpsDateTime(time_t timestamp) {
-  std::string date = ToAsciiDate(timestamp);
-  int32_t seconds = (timestamp % 60);
-  timestamp /= 60;
-  int32_t minutes = (timestamp % 60);
-  timestamp /= 60;
-  mGpsSubIfd->PushTag(new ExifURationalTag(kExifTagGpsTimestamp, timestamp % 24,
-                                           minutes, seconds));
-  mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsDatestamp, date));
-}
-
-void ExifMetadataBuilder::SetLensFocalLength(double length) {
-  mCameraSubIfd->PushTag(
-      new ExifURationalTag(kExifTagCameraFocalLength, length));
-}
-
-void ExifMetadataBuilder::Build() {
-  const uint8_t exif_header[] = {
-      'E', 'x', 'i', 'f', 0, 0,  // EXIF header.
-  };
-
-  const uint8_t tiff_header[] = {
-      'I', 'I', 0x2a, 0x00,  // TIFF Little endian header.
-  };
-
-  // EXIF data should be exactly this much.
-  size_t exif_size = sizeof(exif_header) + sizeof(tiff_header) +
-                     sizeof(uint32_t) +  // Offset of the following descriptors.
-                     mImageIfd->Size() + mThumbnailIfd->Size();
-
-  const uint8_t marker[] = {
-      0xff,
-      0xd8,
-      0xff,
-      0xe1,  // EXIF marker.
-      uint8_t((exif_size + 2) >>
-              8),  // Data length (including the length field)
-      uint8_t((exif_size + 2) & 0xff),
-  };
-
-  // Reserve data for our exif info.
-  mData.Resize(sizeof(marker) + exif_size);
-  uint8_t* b = (uint8_t*)(mData.data());
-
-  // Initialize marker & headers.
-  memcpy(b, &marker, sizeof(marker));
-  b += sizeof(marker);
-  memcpy(b, &exif_header, sizeof(exif_header));
-  b += sizeof(exif_header);
-  memcpy(b, &tiff_header, sizeof(tiff_header));
-  uint32_t data_offset = sizeof(tiff_header);
-
-  // Write offset of the first IFD item.
-  uint32_t first_tag_offset = data_offset + sizeof(uint32_t);
-  memcpy(&b[data_offset], &first_tag_offset, sizeof(first_tag_offset));
-  data_offset += sizeof(first_tag_offset);
-
-  // At this moment |b| points to EXIF structure.
-  // TIFF header is part of the structure itself and explains endianness of the
-  // embedded data.
-  ALOGI("%s: Building Main image EXIF tags", __FUNCTION__);
-  data_offset = mImageIfd->Build(&b[data_offset], data_offset, true);
-  ALOGI("%s: Building Thumbnail image EXIF tags", __FUNCTION__);
-  data_offset = mThumbnailIfd->Build(&b[data_offset], data_offset, false);
-  ALOGI("%s: EXIF metadata constructed (%d bytes).", __FUNCTION__, data_offset);
-}
-
-}  // namespace android
diff --git a/guest/hals/camera/ExifMetadataBuilder.h b/guest/hals/camera/ExifMetadataBuilder.h
deleted file mode 100644
index 57986ba..0000000
--- a/guest/hals/camera/ExifMetadataBuilder.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef EXIFMETADATAWRITER_H_
-#define EXIFMETADATAWRITER_H_
-
-#include <stddef.h>
-#include <stdint.h>
-#include <time.h>
-
-#include <string>
-#include <vector>
-
-#include "common/libs/auto_resources/auto_resources.h"
-
-namespace android {
-class ExifStructure;
-
-// Simplistic EXIF metadata builder.
-// www.exif.org/Exif2-2.PDF
-//
-// TODO(ender): Revisit using libexif after we drop support for JB.
-class ExifMetadataBuilder {
- public:
-  ExifMetadataBuilder();
-  ~ExifMetadataBuilder();
-
-  void SetWidth(int width);
-  void SetHeight(int height);
-  void SetThumbnailWidth(int width);
-  void SetThumbnailHeight(int height);
-  void SetThumbnail(void* thumbnail, int size);
-  void SetGpsLatitude(double degrees);
-  void SetGpsLongitude(double degrees);
-  void SetGpsAltitude(double altitude);
-  void SetGpsDateTime(time_t timestamp);
-  void SetGpsProcessingMethod(const std::string& method);
-  void SetDateTime(time_t t);
-  void SetLensFocalLength(double length);
-  void Build();
-
-  const AutoFreeBuffer& Buffer() { return mData; }
-
- private:
-  ExifStructure* mImageIfd;
-  ExifStructure* mThumbnailIfd;
-  ExifStructure* mCameraSubIfd;
-  ExifStructure* mGpsSubIfd;
-
-  AutoFreeBuffer mData;
-
-  ExifMetadataBuilder(const ExifMetadataBuilder&);
-  ExifMetadataBuilder& operator=(const ExifMetadataBuilder&);
-};
-
-}  // namespace android
-
-#endif  // EXIFMETADATAWRITER_H_
diff --git a/guest/hals/camera/ImageMetadata.h b/guest/hals/camera/ImageMetadata.h
deleted file mode 100644
index b7bf7d4..0000000
--- a/guest/hals/camera/ImageMetadata.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef IMAGEMETADATA_H_
-#define IMAGEMETADATA_H_
-
-// min and max macros make the C++11 support break on nyc-mr1
-#undef min
-#undef max
-#include <stdint.h>
-#include <string>
-
-extern "C" {
-/* Describes various attributes of the picture. */
-struct ImageMetadata {
-  int mWidth;
-  int mHeight;
-  int mThumbnailWidth;
-  int mThumbnailHeight;
-  double mLensFocalLength;
-  double mGpsLatitude;
-  double mGpsLongitude;
-  double mGpsAltitude;
-  time_t mGpsTimestamp;
-  std::string mGpsProcessingMethod;
-};
-}
-
-#endif  // IMAGEMETADATA_H_
diff --git a/guest/hals/camera/JpegCompressor.cpp b/guest/hals/camera/JpegCompressor.cpp
index d8a318c..d2d796d 100644
--- a/guest/hals/camera/JpegCompressor.cpp
+++ b/guest/hals/camera/JpegCompressor.cpp
@@ -37,10 +37,10 @@
   return res;
 }
 
-typedef void (*InitFunc)(JpegStub* stub, int* strides);
+typedef void (*InitFunc)(JpegStub* stub);
 typedef void (*CleanupFunc)(JpegStub* stub);
-typedef int (*CompressFunc)(JpegStub* stub, const void* image, int quality,
-                            const ImageMetadata* meta);
+typedef int (*CompressFunc)(JpegStub* stub, const void* image, int width,
+                            int height, int quality, ExifData* exifData);
 typedef void (*GetCompressedImageFunc)(JpegStub* stub, void* buff);
 typedef size_t (*GetCompressedSizeFunc)(JpegStub* stub);
 
@@ -54,7 +54,7 @@
   assert(mDl != NULL);
 
   InitFunc f = (InitFunc)getSymbol(mDl, "JpegStub_init");
-  (*f)(&mStub, mStrides);
+  (*f)(&mStub);
 }
 
 NV21JpegCompressor::~NV21JpegCompressor() {
@@ -67,12 +67,14 @@
  ***************************************************************************/
 
 status_t NV21JpegCompressor::compressRawImage(const void* image,
-                                              const ImageMetadata* meta,
-                                              int quality) {
-  mStrides[0] = meta->mWidth;
-  mStrides[1] = meta->mWidth;
+                                              ExifData* exifData,
+                                              int quality,
+                                              int width,
+                                              int height) {
+  mStrides[0] = width;
+  mStrides[1] = width;
   CompressFunc f = (CompressFunc)getSymbol(mDl, "JpegStub_compress");
-  return (status_t)(*f)(&mStub, image, quality, meta);
+  return (status_t)(*f)(&mStub, image, width, height, quality, exifData);
 }
 
 size_t NV21JpegCompressor::getCompressedSize() {
diff --git a/guest/hals/camera/JpegCompressor.h b/guest/hals/camera/JpegCompressor.h
index 736d9b5..6d93c46 100644
--- a/guest/hals/camera/JpegCompressor.h
+++ b/guest/hals/camera/JpegCompressor.h
@@ -53,8 +53,8 @@
    *  NO_ERROR on success, or an appropriate error status.
    *
    */
-  status_t compressRawImage(const void* image, const ImageMetadata* metadata,
-                            int quality);
+  status_t compressRawImage(const void* image, ExifData* exifData,
+                            int quality, int width, int height);
 
   /* Get size of the compressed JPEG buffer.
    * This method must be called only after a successful completion of
diff --git a/guest/hals/camera/JpegStub.cpp b/guest/hals/camera/JpegStub.cpp
index f186e03..1eef9e2 100644
--- a/guest/hals/camera/JpegStub.cpp
+++ b/guest/hals/camera/JpegStub.cpp
@@ -14,170 +14,54 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
-#define LOG_TAG "EmulatedCamera_JPEGStub"
-#include <YuvToJpegEncoder.h>
-#include <cutils/log.h>
-#include <errno.h>
-#include <libyuv.h>
-
-#include "ExifMetadataBuilder.h"
 #include "JpegStub.h"
 
-namespace {
-bool GenerateThumbnail(const uint8_t* source_nv21, int source_width,
-                       int source_height, int thumbnail_width,
-                       int thumbnail_height, SkDynamicMemoryWStream* target) {
-  // We need to convert the image to Y'UV420SP to I420, which seems to be the
-  // only scalable format by the LibYUV.
-  // These formats are similar in their memory occupancy (both use about 3/2 of
-  // the total pixels).
-  int temp_y_size = source_width * source_height;
-  int temp_uv_size = temp_y_size / 4;
-  uint8_t* temp_y = (uint8_t*)malloc(temp_y_size + temp_uv_size + temp_uv_size);
-  uint8_t* temp_u = temp_y + temp_y_size;
-  uint8_t* temp_v = temp_u + temp_uv_size;
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_JPEGStub"
+#include <errno.h>
+#include <cutils/log.h>
+#include <stdlib.h>
 
-  libyuv::NV12ToI420(source_nv21, source_width, source_nv21 + temp_y_size,
-                     source_width, temp_y, source_width, temp_u,
-                     source_width / 2, temp_v, source_width / 2, source_width,
-                     source_height);
+#include "Compressor.h"
 
-  // Compute and allocate memory for thumbnail I420.
-  int thumb_y_size = thumbnail_width * thumbnail_height;
-  int thumb_uv_size = thumb_y_size / 4;
-  uint8_t* thumb_y =
-      (uint8_t*)malloc(thumb_y_size + thumb_uv_size + thumb_uv_size);
-  uint8_t* thumb_u = thumb_y + thumb_y_size;
-  uint8_t* thumb_v = thumb_u + thumb_uv_size;
-
-  libyuv::I420Scale(temp_y, source_width, temp_u, source_width / 2, temp_v,
-                    source_width / 2, source_width, source_height, thumb_y,
-                    thumbnail_width, thumb_u, thumbnail_width / 2, thumb_v,
-                    thumbnail_width / 2, thumbnail_width, thumbnail_height,
-                    libyuv::kFilterBilinear);
-
-  // Combine U and V components back to NV21 format.
-  // We can re-use temp_y buffer for our needs at this point.
-  for (int pix = 0; pix < thumb_uv_size; ++pix) {
-    temp_y[2 * pix] = thumb_v[pix];
-    temp_y[2 * pix + 1] = thumb_u[pix];
-  }
-
-  // Put the memory back. After this, the thumb_y points to beginning of NV21
-  // image which we can compress.
-  memcpy(thumb_u, temp_y, thumb_uv_size * 2);
-
-  // Compress image.
-  int strides[2] = {thumbnail_width, thumbnail_width};
-  int offsets[2] = {0, thumb_y_size};
-  Yuv420SpToJpegEncoder* encoder = new Yuv420SpToJpegEncoder(strides);
-
-  bool result = encoder->encode(target, thumb_y, thumbnail_width,
-                                thumbnail_height, offsets, 90);
-
-  if (!result) {
-    ALOGE("%s: Thumbnail compression failed", __FUNCTION__);
-  }
-
-  delete (encoder);
-  free(thumb_y);
-  free(temp_y);
-
-  return result;
-}
-}  // namespace
-
-extern "C" void JpegStub_init(JpegStub* stub, int* strides) {
-  stub->mInternalEncoder = (void*)new Yuv420SpToJpegEncoder(strides);
-  stub->mInternalStream = (void*)new SkDynamicMemoryWStream();
-  stub->mExifBuilder = (void*)new android::ExifMetadataBuilder();
+extern "C" void JpegStub_init(JpegStub* stub) {
+    stub->mCompressor = static_cast<void*>(new Compressor());
 }
 
 extern "C" void JpegStub_cleanup(JpegStub* stub) {
-  delete ((Yuv420SpToJpegEncoder*)stub->mInternalEncoder);
-  delete ((SkDynamicMemoryWStream*)stub->mInternalStream);
-  delete ((android::ExifMetadataBuilder*)stub->mExifBuilder);
+    delete reinterpret_cast<Compressor*>(stub->mCompressor);
+    stub->mCompressor = nullptr;
 }
 
-extern "C" int JpegStub_compress(JpegStub* stub, const void* image, int quality,
-                                 const ImageMetadata* meta) {
-  void* pY = const_cast<void*>(image);
+extern "C" int JpegStub_compress(JpegStub* stub,
+                                 const void* buffer,
+                                 int width,
+                                 int height,
+                                 int quality,
+                                 ExifData* exifData)
+{
+    Compressor* compressor = reinterpret_cast<Compressor*>(stub->mCompressor);
 
-  int offsets[2];
-  offsets[0] = 0;
-  offsets[1] = meta->mWidth * meta->mHeight;
-
-  Yuv420SpToJpegEncoder* encoder =
-      (Yuv420SpToJpegEncoder*)stub->mInternalEncoder;
-  SkDynamicMemoryWStream* stream =
-      (SkDynamicMemoryWStream*)stub->mInternalStream;
-  android::ExifMetadataBuilder* exif =
-      (android::ExifMetadataBuilder*)stub->mExifBuilder;
-
-  exif->SetWidth(meta->mWidth);
-  exif->SetHeight(meta->mHeight);
-  exif->SetDateTime(time(NULL));
-  if (meta->mLensFocalLength != -1)
-    exif->SetLensFocalLength(meta->mLensFocalLength);
-  if (meta->mGpsTimestamp != -1) {
-    exif->SetGpsLatitude(meta->mGpsLatitude);
-    exif->SetGpsLongitude(meta->mGpsLongitude);
-    exif->SetGpsAltitude(meta->mGpsAltitude);
-    exif->SetGpsDateTime(meta->mGpsTimestamp);
-    exif->SetGpsProcessingMethod(meta->mGpsProcessingMethod);
-  }
-
-  ALOGV("%s: Requested thumbnail size: %dx%d", __FUNCTION__,
-        meta->mThumbnailWidth, meta->mThumbnailHeight);
-
-  // Thumbnail requested?
-  if (meta->mThumbnailWidth > 0 && meta->mThumbnailHeight > 0) {
-    exif->SetThumbnailWidth(meta->mThumbnailWidth);
-    exif->SetThumbnailHeight(meta->mThumbnailHeight);
-    SkDynamicMemoryWStream* thumbnail = new SkDynamicMemoryWStream();
-    GenerateThumbnail((uint8_t*)pY, meta->mWidth, meta->mHeight,
-                      meta->mThumbnailWidth, meta->mThumbnailHeight, thumbnail);
-
-    int thumbnail_size = thumbnail->bytesWritten();
-    void* thumbnail_data = malloc(thumbnail_size);
-    thumbnail->read(thumbnail_data, 0, thumbnail_size);
-    // Pass ownership to EXIF builder.
-    exif->SetThumbnail(thumbnail_data, thumbnail_size);
-    delete thumbnail;
-  }
-
-  exif->Build();
-
-  if (encoder->encode(stream, pY, meta->mWidth, meta->mHeight, offsets,
-                      quality)) {
-    ALOGI("%s: Compressed JPEG: %d[%dx%d] -> %zu bytes", __FUNCTION__,
-          (meta->mWidth * meta->mHeight * 12) / 8, meta->mWidth, meta->mHeight,
-          stream->bytesWritten());
-    return 0;
-  } else {
+    if (compressor->compress(reinterpret_cast<const unsigned char*>(buffer),
+                              width, height, quality, exifData)) {
+        ALOGV("%s: Compressed JPEG: %d[%dx%d] -> %zu bytes",
+              __FUNCTION__, (width * height * 12) / 8,
+              width, height, compressor->getCompressedData().size());
+        return 0;
+    }
     ALOGE("%s: JPEG compression failed", __FUNCTION__);
     return errno ? errno : EINVAL;
-  }
 }
 
 extern "C" void JpegStub_getCompressedImage(JpegStub* stub, void* buff) {
-  SkDynamicMemoryWStream* stream =
-      (SkDynamicMemoryWStream*)stub->mInternalStream;
-  android::ExifMetadataBuilder* exif =
-      (android::ExifMetadataBuilder*)stub->mExifBuilder;
-  char* target = (char*)buff;
-  memcpy(buff, exif->Buffer().data(), exif->Buffer().size());
-  target += exif->Buffer().size();
+    Compressor* compressor = reinterpret_cast<Compressor*>(stub->mCompressor);
 
-  // Skip 0xFFD8 marker. This marker has already been included in Metadata.
-  stream->read(target, 2, stream->bytesWritten() - 2);
+    const std::vector<unsigned char>& data = compressor->getCompressedData();
+    memcpy(buff, &data[0], data.size());
 }
 
 extern "C" size_t JpegStub_getCompressedSize(JpegStub* stub) {
-  SkDynamicMemoryWStream* stream =
-      (SkDynamicMemoryWStream*)stub->mInternalStream;
-  android::ExifMetadataBuilder* exif =
-      (android::ExifMetadataBuilder*)stub->mExifBuilder;
-  return stream->bytesWritten() + exif->Buffer().size() - 2;
+    Compressor* compressor = reinterpret_cast<Compressor*>(stub->mCompressor);
+
+    return compressor->getCompressedData().size();
 }
diff --git a/guest/hals/camera/JpegStub.h b/guest/hals/camera/JpegStub.h
index 639f906..2e62182 100644
--- a/guest/hals/camera/JpegStub.h
+++ b/guest/hals/camera/JpegStub.h
@@ -17,21 +17,27 @@
 #ifndef JPEGSTUB_H_
 #define JPEGSTUB_H_
 
-#include "ImageMetadata.h"
+#include <stddef.h>
+
+struct _ExifData;
+typedef _ExifData ExifData;
 
 extern "C" {
 
 struct JpegStub {
-  void* mInternalEncoder;
-  void* mInternalStream;
-  void* mExifBuilder;
+    void* mCompressor;
 };
 
-void JpegStub_init(JpegStub* stub, int* strides);
+void JpegStub_init(JpegStub* stub);
 void JpegStub_cleanup(JpegStub* stub);
-int JpegStub_compress(JpegStub* stub, const void* image, int quality,
-                      const ImageMetadata* metadata);
+int JpegStub_compress(JpegStub* stub,
+                      const void* image,
+                      int width,
+                      int height,
+                      int quality,
+                      ExifData* exifData);
 void JpegStub_getCompressedImage(JpegStub* stub, void* buff);
 size_t JpegStub_getCompressedSize(JpegStub* stub);
+
 };
-#endif  // JPEGSTUB_H_
+#endif // JPEGSTUB_H_
diff --git a/guest/hals/camera/Thumbnail.cpp b/guest/hals/camera/Thumbnail.cpp
new file mode 100644
index 0000000..4b431bb
--- /dev/null
+++ b/guest/hals/camera/Thumbnail.cpp
@@ -0,0 +1,170 @@
+/*
+* Copyright (C) 2016 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.
+ */
+
+#include "Thumbnail.h"
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_Thumbnail"
+#include <cutils/log.h>
+#include <libexif/exif-data.h>
+#include <libyuv.h>
+
+#include "JpegCompressor.h"
+
+#include <vector>
+
+/*
+ * The NV21 format is a YUV format with an 8-bit Y-component and the U and V
+ * components are stored as 8 bits each but they are shared between a block of
+ * 2x2 pixels. So when calculating bits per pixel the 16 bits of U and V are
+ * shared between 4 pixels leading to 4 bits of U and V per pixel. Together
+ * with the 8 bits of Y this gives us 12 bits per pixel..
+ *
+ * The components are not grouped by pixels but separated into one Y-plane and
+ * one interleaved U and V-plane. The first half of the byte sequence is all of
+ * the Y data laid out in a linear fashion. After that the interleaved U and V-
+ * plane starts with one byte of V followed by one byte of U followed by one
+ * byte of V and so on. Each byte of U or V is associated with a 2x2 pixel block
+ * in a linear fashion.
+ *
+ * For an 8 by 4 pixel image the layout would be:
+ *
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | Y0  | Y1  | Y2  | Y3  | Y4  | Y5  | Y6  | Y7  |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | Y8  | Y9  | Y10 | Y11 | Y12 | Y13 | Y14 | Y15 |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | Y16 | Y17 | Y18 | Y19 | Y20 | Y21 | Y22 | Y23 |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | Y24 | Y25 | Y26 | Y27 | Y28 | Y29 | Y30 | Y31 |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | V0  | U0  | V1  | U1  | V2  | U2  | V3  | U3  |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | V4  | U4  | V5  | U5  | V6  | U6  | V7  | U7  |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ * In this image V0 and U0 are the V and U components for the 2x2 block of
+ * pixels whose Y components are Y0, Y1, Y8 and Y9. V1 and U1 are matched with
+ * the Y components Y2, Y3, Y10, Y11, and so on for that row. For the next row
+ * of V and U the V4 and U4 components would be paired with Y16, Y17, Y24 and
+ * Y25.
+ */
+
+namespace android {
+
+static bool createRawThumbnail(const unsigned char* sourceImage,
+                               int sourceWidth, int sourceHeight,
+                               int thumbnailWidth, int thumbnailHeight,
+                               std::vector<unsigned char>* thumbnail) {
+    // Deinterleave the U and V planes into separate planes, this is because
+    // libyuv requires the planes to be separate when scaling
+    const size_t sourceUVPlaneSize = (sourceWidth * sourceHeight) / 4;
+    // Put both U and V planes in one buffer, one after the other, to reduce
+    // memory fragmentation and number of allocations
+    std::vector<unsigned char> sourcePlanes(sourceUVPlaneSize * 2);
+    const unsigned char* ySourcePlane = sourceImage;
+    unsigned char* uSourcePlane = &sourcePlanes[0];
+    unsigned char* vSourcePlane = &sourcePlanes[sourceUVPlaneSize];
+
+    for (size_t i = 0; i < sourceUVPlaneSize; ++i) {
+        vSourcePlane[i] = sourceImage[sourceWidth * sourceHeight + i * 2 + 0];
+        uSourcePlane[i] = sourceImage[sourceWidth * sourceHeight + i * 2 + 1];
+    }
+
+    // Create enough space in the output vector for the result
+    thumbnail->resize((thumbnailWidth * thumbnailHeight * 12) / 8);
+
+    // The downscaled U and V planes will also be linear instead of interleaved,
+    // allocate space for them here
+    const size_t destUVPlaneSize = (thumbnailWidth * thumbnailHeight) / 4;
+    std::vector<unsigned char> destPlanes(destUVPlaneSize * 2);
+    unsigned char* yDestPlane = &(*thumbnail)[0];
+    unsigned char* uDestPlane = &destPlanes[0];
+    unsigned char* vDestPlane = &destPlanes[destUVPlaneSize];
+
+    // The strides for the U and V planes are half the width because the U and V
+    // components are common to 2x2 pixel blocks
+    int result = libyuv::I420Scale(ySourcePlane, sourceWidth,
+                                   uSourcePlane, sourceWidth / 2,
+                                   vSourcePlane, sourceWidth / 2,
+                                   sourceWidth, sourceHeight,
+                                   yDestPlane, thumbnailWidth,
+                                   uDestPlane, thumbnailWidth / 2,
+                                   vDestPlane, thumbnailWidth / 2,
+                                   thumbnailWidth, thumbnailHeight,
+                                   libyuv::kFilterBilinear);
+    if (result != 0) {
+        ALOGE("Unable to create thumbnail, downscaling failed with error: %d",
+              result);
+        return false;
+    }
+
+    // Now we need to interleave the downscaled U and V planes into the
+    // output buffer to make it NV21 encoded
+    const size_t uvPlanesOffset = thumbnailWidth * thumbnailHeight;
+    for (size_t i = 0; i < destUVPlaneSize; ++i) {
+        (*thumbnail)[uvPlanesOffset + i * 2 + 0] = vDestPlane[i];
+        (*thumbnail)[uvPlanesOffset + i * 2 + 1] = uDestPlane[i];
+    }
+
+    return true;
+}
+
+bool createThumbnail(const unsigned char* sourceImage,
+                     int sourceWidth, int sourceHeight,
+                     int thumbWidth, int thumbHeight, int quality,
+                     ExifData* exifData) {
+    if (thumbWidth <= 0 || thumbHeight <= 0) {
+        ALOGE("%s: Invalid thumbnail width=%d or height=%d, must be > 0",
+              __FUNCTION__, thumbWidth, thumbHeight);
+        return false;
+    }
+
+    // First downscale the source image into a thumbnail-sized raw image
+    std::vector<unsigned char> rawThumbnail;
+    if (!createRawThumbnail(sourceImage, sourceWidth, sourceHeight,
+                            thumbWidth, thumbHeight, &rawThumbnail)) {
+        // The thumbnail function will log an appropriate error if needed
+        return false;
+    }
+
+    // And then compress it into JPEG format without any EXIF data
+    NV21JpegCompressor compressor;
+    status_t result = compressor.compressRawImage(&rawThumbnail[0],
+                                                  nullptr /* EXIF */,
+                                                  quality, thumbWidth, thumbHeight);
+    if (result != NO_ERROR) {
+        ALOGE("%s: Unable to compress thumbnail", __FUNCTION__);
+        return false;
+    }
+
+    // And finally put it in the EXIF data. This transfers ownership of the
+    // malloc'd memory to the EXIF data structure. As long as the EXIF data
+    // structure is free'd using the EXIF library this memory will be free'd.
+    exifData->size = compressor.getCompressedSize();
+    exifData->data = reinterpret_cast<unsigned char*>(malloc(exifData->size));
+    if (exifData->data == nullptr) {
+        ALOGE("%s: Unable to allocate %u bytes of memory for thumbnail",
+              __FUNCTION__, exifData->size);
+        exifData->size = 0;
+        return false;
+    }
+    compressor.getCompressedImage(exifData->data);
+    return true;
+}
+
+}  // namespace android
+
diff --git a/guest/hals/camera/Thumbnail.h b/guest/hals/camera/Thumbnail.h
new file mode 100644
index 0000000..b27636c
--- /dev/null
+++ b/guest/hals/camera/Thumbnail.h
@@ -0,0 +1,37 @@
+/*
+* Copyright (C) 2016 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.
+ */
+
+#ifndef GOLDFISH_CAMERA_THUMBNAIL_H
+#define GOLDFISH_CAMERA_THUMBNAIL_H
+
+struct _ExifData;
+typedef struct _ExifData ExifData;
+
+namespace android {
+
+/* Create a thumbnail from NV21 source data in |sourceImage| with the given
+ * dimensions. The resulting thumbnail is JPEG compressed and a pointer and size
+ * is placed in |exifData| which takes ownership of the allocated memory.
+ */
+bool createThumbnail(const unsigned char* sourceImage,
+                     int sourceWidth, int sourceHeight,
+                     int thumbnailWidth, int thumbnailHeight, int quality,
+                     ExifData* exifData);
+
+}  // namespace android
+
+#endif  // GOLDFISH_CAMERA_THUMBNAIL_H
+