Merge "Add multiple format capability to AudioMixer"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index b8a9711..20da925 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -51,6 +51,10 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudioflinger.so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicy_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicy.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicy_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libaudiopolicy.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicyservice_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libaudiopolicymanager_intermediates)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/camera/Android.mk b/camera/Android.mk
index 5774b6f..c10e38a 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -50,7 +50,7 @@
 
 LOCAL_C_INCLUDES += \
 	system/media/camera/include \
-	system/media/private/camera/include
+	system/media/private/camera/include \
 
 LOCAL_MODULE:= libcamera_client
 
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index af091f4..161f842 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -21,6 +21,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <camera/CameraParameters.h>
+#include <system/graphics.h>
 
 namespace android {
 // Parameter keys to communicate between camera application and driver.
@@ -483,4 +484,45 @@
     return NO_ERROR;
 }
 
+void CameraParameters::getSupportedPreviewFormats(Vector<int>& formats) const {
+    const char* supportedPreviewFormats =
+          get(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS);
+
+    String8 fmtStr(supportedPreviewFormats);
+    char* prevFmts = fmtStr.lockBuffer(fmtStr.size());
+
+    char* savePtr;
+    char* fmt = strtok_r(prevFmts, ",", &savePtr);
+    while (fmt) {
+        int actual = previewFormatToEnum(fmt);
+        if (actual != -1) {
+            formats.add(actual);
+        }
+        fmt = strtok_r(NULL, ",", &savePtr);
+    }
+    fmtStr.unlockBuffer(fmtStr.size());
+}
+
+
+int CameraParameters::previewFormatToEnum(const char* format) {
+    return
+        !format ?
+            HAL_PIXEL_FORMAT_YCrCb_420_SP :
+        !strcmp(format, PIXEL_FORMAT_YUV422SP) ?
+            HAL_PIXEL_FORMAT_YCbCr_422_SP : // NV16
+        !strcmp(format, PIXEL_FORMAT_YUV420SP) ?
+            HAL_PIXEL_FORMAT_YCrCb_420_SP : // NV21
+        !strcmp(format, PIXEL_FORMAT_YUV422I) ?
+            HAL_PIXEL_FORMAT_YCbCr_422_I :  // YUY2
+        !strcmp(format, PIXEL_FORMAT_YUV420P) ?
+            HAL_PIXEL_FORMAT_YV12 :         // YV12
+        !strcmp(format, PIXEL_FORMAT_RGB565) ?
+            HAL_PIXEL_FORMAT_RGB_565 :      // RGB565
+        !strcmp(format, PIXEL_FORMAT_RGBA8888) ?
+            HAL_PIXEL_FORMAT_RGBA_8888 :    // RGB8888
+        !strcmp(format, PIXEL_FORMAT_BAYER_RGGB) ?
+            HAL_PIXEL_FORMAT_RAW_SENSOR :   // Raw sensor data
+        -1;
+}
+
 }; // namespace android
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index 59dce91..3f72f34 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -349,18 +349,18 @@
 
     size_t size = mTagToNameMap.size();
     if (size == 0) {
-        fdprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n",
+        dprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n",
                 indentation, "");
         return;
     }
 
-    fdprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n",
+    dprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n",
             indentation, "", size);
     for (size_t i = 0; i < size; ++i) {
         uint32_t tag =  mTagToNameMap.keyAt(i);
 
         if (verbosity < 1) {
-            fdprintf(fd, "%*s0x%x\n", indentation + 2, "", tag);
+            dprintf(fd, "%*s0x%x\n", indentation + 2, "", tag);
             continue;
         }
         String8 name = mTagToNameMap.valueAt(i);
@@ -369,7 +369,7 @@
         int type = mTagToTypeMap.valueFor(tag);
         const char* typeName = (type >= 0 && type < NUM_TYPES) ?
                 camera_metadata_type_names[type] : "UNKNOWN";
-        fdprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2,
+        dprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2,
             "", tag, name.string(), type, typeName, sectionName.string());
     }
 
diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp
index ad65955..ff4a0c2 100644
--- a/camera/camera2/ICameraDeviceUser.cpp
+++ b/camera/camera2/ICameraDeviceUser.cpp
@@ -37,6 +37,8 @@
     SUBMIT_REQUEST,
     SUBMIT_REQUEST_LIST,
     CANCEL_REQUEST,
+    BEGIN_CONFIGURE,
+    END_CONFIGURE,
     DELETE_STREAM,
     CREATE_STREAM,
     CREATE_DEFAULT_REQUEST,
@@ -174,6 +176,26 @@
         return res;
     }
 
+    virtual status_t beginConfigure()
+    {
+        ALOGV("beginConfigure");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+        remote()->transact(BEGIN_CONFIGURE, data, &reply);
+        reply.readExceptionCode();
+        return reply.readInt32();
+    }
+
+    virtual status_t endConfigure()
+    {
+        ALOGV("endConfigure");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
+        remote()->transact(END_CONFIGURE, data, &reply);
+        reply.readExceptionCode();
+        return reply.readInt32();
+    }
+
     virtual status_t deleteStream(int streamId)
     {
         Parcel data, reply;
@@ -456,6 +478,18 @@
             reply->writeInt64(lastFrameNumber);
             return NO_ERROR;
         }
+        case BEGIN_CONFIGURE: {
+            CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+            reply->writeNoException();
+            reply->writeInt32(beginConfigure());
+            return NO_ERROR;
+        } break;
+        case END_CONFIGURE: {
+            CHECK_INTERFACE(ICameraDeviceUser, data, reply);
+            reply->writeNoException();
+            reply->writeInt32(endConfigure());
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index d521543..c6074fc 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -102,6 +102,12 @@
     void dump() const;
     status_t dump(int fd, const Vector<String16>& args) const;
 
+    /**
+     * Returns a Vector containing the supported preview formats
+     * as enums given in graphics.h.
+     */
+    void getSupportedPreviewFormats(Vector<int>& formats) const;
+
     // Parameter keys to communicate between camera application and driver.
     // The access (read/write, read only, or write only) is viewed from the
     // perspective of applications, not driver.
@@ -674,6 +680,13 @@
     // High-dynamic range mode
     static const char LIGHTFX_HDR[];
 
+    /**
+     * Returns the the supported preview formats as an enum given in graphics.h
+     * corrsponding to the format given in the input string or -1 if no such
+     * conversion exists.
+     */
+    static int previewFormatToEnum(const char* format);
+
 private:
     DefaultKeyedVector<String8,String8>    mMap;
 };
diff --git a/include/camera/camera2/ICameraDeviceUser.h b/include/camera/camera2/ICameraDeviceUser.h
index 913696f..35488bb 100644
--- a/include/camera/camera2/ICameraDeviceUser.h
+++ b/include/camera/camera2/ICameraDeviceUser.h
@@ -78,6 +78,27 @@
                                           /*out*/
                                           int64_t* lastFrameNumber = NULL) = 0;
 
+    /**
+     * Begin the device configuration.
+     *
+     * <p>
+     * beginConfigure must be called before any call to deleteStream, createStream,
+     * or endConfigure.  It is not valid to call this when the device is not idle.
+     * <p>
+     */
+    virtual status_t        beginConfigure() = 0;
+
+    /**
+     * End the device configuration.
+     *
+     * <p>
+     * endConfigure must be called after stream configuration is complete (i.e. after
+     * a call to beginConfigure and subsequent createStream/deleteStream calls).  This
+     * must be called before any requests can be submitted.
+     * <p>
+     */
+    virtual status_t        endConfigure() = 0;
+
     virtual status_t        deleteStream(int streamId) = 0;
     virtual status_t        createStream(
             int width, int height, int format,
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 678d642..563c0b5 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -25,6 +25,7 @@
 extern const char *MEDIA_MIMETYPE_VIDEO_VP8;
 extern const char *MEDIA_MIMETYPE_VIDEO_VP9;
 extern const char *MEDIA_MIMETYPE_VIDEO_AVC;
+extern const char *MEDIA_MIMETYPE_VIDEO_HEVC;
 extern const char *MEDIA_MIMETYPE_VIDEO_MPEG4;
 extern const char *MEDIA_MIMETYPE_VIDEO_H263;
 extern const char *MEDIA_MIMETYPE_VIDEO_MPEG2;
diff --git a/include/ndk/NdkMediaCodec.h b/include/ndk/NdkMediaCodec.h
index dd869f6..2f000d7 100644
--- a/include/ndk/NdkMediaCodec.h
+++ b/include/ndk/NdkMediaCodec.h
@@ -146,10 +146,22 @@
 AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec*);
 
 /**
- * Release and optionally render the specified buffer.
+ * If you are done with a buffer, use this call to return the buffer to
+ * the codec. If you previously specified a surface when configuring this
+ * video decoder you can optionally render the buffer.
  */
 media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render);
 
+/**
+ * If you are done with a buffer, use this call to update its surface timestamp
+ * and return it to the codec to render it on the output surface. If you
+ * have not specified an output surface when configuring this video codec,
+ * this call will simply return the buffer to the codec.
+ *
+ * For more details, see the Java documentation for MediaCodec.releaseOutputBuffer.
+ */
+media_status_t AMediaCodec_releaseOutputBufferAtTime(
+        AMediaCodec *mData, size_t idx, int64_t timestampNs);
 
 typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
 
@@ -163,20 +175,30 @@
         AMediaCodec*, OnCodecEvent callback, void *userdata);
 
 
-enum {
+typedef enum {
     AMEDIACODECRYPTOINFO_MODE_CLEAR = 0,
     AMEDIACODECRYPTOINFO_MODE_AES_CTR = 1
-};
+} cryptoinfo_mode_t;
 
 /**
- * create an AMediaCodecCryptoInfo from scratch. Use this if you need to use custom
+ * Create an AMediaCodecCryptoInfo from scratch. Use this if you need to use custom
  * crypto info, rather than one obtained from AMediaExtractor.
+ *
+ * AMediaCodecCryptoInfo describes the structure of an (at least
+ * partially) encrypted input sample.
+ * A buffer's data is considered to be partitioned into "subsamples",
+ * each subsample starts with a (potentially empty) run of plain,
+ * unencrypted bytes followed by a (also potentially empty) run of
+ * encrypted bytes.
+ * numBytesOfClearData can be null to indicate that all data is encrypted.
+ * This information encapsulates per-sample metadata as outlined in
+ * ISO/IEC FDIS 23001-7:2011 "Common encryption in ISO base media file format files".
  */
 AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new(
         int numsubsamples,
         uint8_t key[16],
         uint8_t iv[16],
-        uint32_t mode,
+        cryptoinfo_mode_t mode,
         size_t *clearbytes,
         size_t *encryptedbytes);
 
@@ -186,11 +208,35 @@
  */
 media_status_t AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo*);
 
+/**
+ * The number of subsamples that make up the buffer's contents.
+ */
 size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo*);
+
+/**
+ * A 16-byte opaque key
+ */
 media_status_t AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo*, uint8_t *dst);
+
+/**
+ * A 16-byte initialization vector
+ */
 media_status_t AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo*, uint8_t *dst);
-uint32_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo*);
+
+/**
+ * The type of encryption that has been applied,
+ * one of AMEDIACODECRYPTOINFO_MODE_CLEAR or AMEDIACODECRYPTOINFO_MODE_AES_CTR.
+ */
+cryptoinfo_mode_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo*);
+
+/**
+ * The number of leading unencrypted bytes in each subsample.
+ */
 media_status_t AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo*, size_t *dst);
+
+/**
+ * The number of trailing encrypted bytes in each subsample.
+ */
 media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo*, size_t *dst);
 
 #ifdef __cplusplus
diff --git a/include/ndk/NdkMediaCrypto.h b/include/ndk/NdkMediaCrypto.h
index 83eaad2..90374c5 100644
--- a/include/ndk/NdkMediaCrypto.h
+++ b/include/ndk/NdkMediaCrypto.h
@@ -29,6 +29,7 @@
 #define _NDK_MEDIA_CRYPTO_H
 
 #include <sys/types.h>
+#include <stdbool.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/include/ndk/NdkMediaDrm.h b/include/ndk/NdkMediaDrm.h
index 04c371c..10afdd9 100644
--- a/include/ndk/NdkMediaDrm.h
+++ b/include/ndk/NdkMediaDrm.h
@@ -27,7 +27,7 @@
 #ifndef _NDK_MEDIA_DRM_H
 #define _NDK_MEDIA_DRM_H
 
-#include <NdkMediaError.h>
+#include "NdkMediaError.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -77,7 +77,7 @@
     EVENT_VENDOR_DEFINED = 4
 } AMediaDrmEventType;
 
-typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId *sessionId,
         AMediaDrmEventType eventType, int extra, const uint8_t *data, size_t dataSize);
 
 
@@ -115,13 +115,13 @@
  * returns MEDIADRM_NOT_PROVISIONED_ERROR if provisioning is needed
  * returns MEDIADRM_RESOURCE_BUSY_ERROR if required resources are in use
  */
-media_status_t AMediaDrm_openSession(AMediaDrm *, AMediaDrmSessionId &sessionId);
+media_status_t AMediaDrm_openSession(AMediaDrm *, AMediaDrmSessionId *sessionId);
 
 /**
  * Close a session on the MediaDrm object that was previously opened
  * with AMediaDrm_openSession.
  */
-media_status_t AMediaDrm_closeSession(AMediaDrm *, const AMediaDrmSessionId &sessionId);
+media_status_t AMediaDrm_closeSession(AMediaDrm *, const AMediaDrmSessionId *sessionId);
 
 typedef enum AMediaDrmKeyType {
     /**
@@ -198,10 +198,10 @@
  * returns MEDIADRM_NOT_PROVISIONED_ERROR if reprovisioning is needed, due to a
  * problem with the device certificate.
 */
-media_status_t AMediaDrm_getKeyRequest(AMediaDrm *, const AMediaDrmScope &scope,
+media_status_t AMediaDrm_getKeyRequest(AMediaDrm *, const AMediaDrmScope *scope,
         const uint8_t *init, size_t initSize, const char *mimeType, AMediaDrmKeyType keyType,
         const AMediaDrmKeyValue *optionalParameters, size_t numOptionalParameters,
-        const uint8_t *&keyRequest, size_t &keyRequestSize);
+        const uint8_t **keyRequest, size_t *keyRequestSize);
 
 /**
  * A key response is received from the license server by the app, then it is
@@ -220,8 +220,8 @@
  * responseSize should be set to the size of the response in bytes
  */
 
-media_status_t AMediaDrm_provideKeyResponse(AMediaDrm *, const AMediaDrmScope &scope,
-        const uint8_t *response, size_t responseSize, AMediaDrmKeySetId &keySetId);
+media_status_t AMediaDrm_provideKeyResponse(AMediaDrm *, const AMediaDrmScope *scope,
+        const uint8_t *response, size_t responseSize, AMediaDrmKeySetId *keySetId);
 
 /**
  * Restore persisted offline keys into a new session.  keySetId identifies the
@@ -230,15 +230,15 @@
  * sessionId is the session ID for the DRM session
  * keySetId identifies the saved key set to restore
  */
-media_status_t AMediaDrm_restoreKeys(AMediaDrm *, const AMediaDrmSessionId &sessionId,
-        const AMediaDrmKeySetId &keySetId);
+media_status_t AMediaDrm_restoreKeys(AMediaDrm *, const AMediaDrmSessionId *sessionId,
+        const AMediaDrmKeySetId *keySetId);
 
 /**
  * Remove the current keys from a session.
  *
  * keySetId identifies keys to remove
  */
-media_status_t AMediaDrm_removeKeys(AMediaDrm *, const AMediaDrmSessionId &keySetId);
+media_status_t AMediaDrm_removeKeys(AMediaDrm *, const AMediaDrmSessionId *keySetId);
 
 /**
  * Request an informative description of the key status for the session.  The status is
@@ -253,8 +253,8 @@
  * to be returned is greater than *numPairs, MEDIADRM_SHORT_BUFFER will be returned
  * and numPairs will be set to the number of pairs available.
  */
-media_status_t AMediaDrm_queryKeyStatus(AMediaDrm *, const AMediaDrmSessionId &sessionId,
-        AMediaDrmKeyValue *keyValuePairs, size_t &numPairs);
+media_status_t AMediaDrm_queryKeyStatus(AMediaDrm *, const AMediaDrmSessionId *sessionId,
+        AMediaDrmKeyValue *keyValuePairs, size_t *numPairs);
 
 
 /**
@@ -272,8 +272,8 @@
  *       the provisioning request should be sent to.  It will remain accessible until
  *       the next call to getProvisionRequest.
  */
-media_status_t AMediaDrm_getProvisionRequest(AMediaDrm *, const uint8_t *&provisionRequest,
-        size_t &provisionRequestSize, const char *&serverUrl);
+media_status_t AMediaDrm_getProvisionRequest(AMediaDrm *, const uint8_t **provisionRequest,
+        size_t *provisionRequestSize, const char **serverUrl);
 
 
 /**
@@ -313,7 +313,7 @@
  * number required.
  */
 media_status_t AMediaDrm_getSecureStops(AMediaDrm *,
-        AMediaDrmSecureStop *secureStops, size_t &numSecureStops);
+        AMediaDrmSecureStop *secureStops, size_t *numSecureStops);
 
 /**
  * Process the SecureStop server response message ssRelease.  After authenticating
@@ -322,7 +322,7 @@
  * ssRelease is the server response indicating which secure stops to release
  */
 media_status_t AMediaDrm_releaseSecureStops(AMediaDrm *,
-        const AMediaDrmSecureStop &ssRelease);
+        const AMediaDrmSecureStop *ssRelease);
 
 /**
  * String property name: identifies the maker of the DRM engine plugin
@@ -355,7 +355,7 @@
  * will remain valid until the next call to AMediaDrm_getPropertyString.
  */
 media_status_t AMediaDrm_getPropertyString(AMediaDrm *, const char *propertyName,
-        const char *&propertyValue);
+        const char **propertyValue);
 
 /**
  * Byte array property name: the device unique identifier is established during
@@ -370,7 +370,7 @@
  * will remain valid until the next call to AMediaDrm_getPropertyByteArray.
  */
 media_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *, const char *propertyName,
-        AMediaDrmByteArray &propertyValue);
+        AMediaDrmByteArray *propertyValue);
 
 /**
  * Set a DRM engine plugin String property value.
@@ -409,7 +409,7 @@
  * to use is identified by the 16 byte keyId.  The key must have been loaded into
  * the session using provideKeyResponse.
  */
-media_status_t AMediaDrm_encrypt(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+media_status_t AMediaDrm_encrypt(AMediaDrm *, const AMediaDrmSessionId *sessionId,
         const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
         const uint8_t *input, uint8_t *output, size_t dataSize);
 
@@ -420,7 +420,7 @@
  * to use is identified by the 16 byte keyId.  The key must have been loaded into
  * the session using provideKeyResponse.
  */
-media_status_t AMediaDrm_decrypt(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+media_status_t AMediaDrm_decrypt(AMediaDrm *, const AMediaDrmSessionId *sessionId,
         const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
         const uint8_t *input, uint8_t *output, size_t dataSize);
 
@@ -433,7 +433,7 @@
  * by the 16 byte keyId.  The key must have been loaded into the session using
  * provideKeyResponse.
  */
-media_status_t AMediaDrm_sign(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+media_status_t AMediaDrm_sign(AMediaDrm *, const AMediaDrmSessionId *sessionId,
         const char *macAlgorithm, uint8_t *keyId, uint8_t *message, size_t messageSize,
         uint8_t *signature, size_t *signatureSize);
 
@@ -444,7 +444,7 @@
  * use is identified by the 16 byte keyId.  The key must have been loaded into the
  * session using provideKeyResponse.
  */
-media_status_t AMediaDrm_verify(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+media_status_t AMediaDrm_verify(AMediaDrm *, const AMediaDrmSessionId *sessionId,
         const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize,
         const uint8_t *signature, size_t signatureSize);
 
diff --git a/include/ndk/NdkMediaExtractor.h b/include/ndk/NdkMediaExtractor.h
index 2ba69fb..5a319d7 100644
--- a/include/ndk/NdkMediaExtractor.h
+++ b/include/ndk/NdkMediaExtractor.h
@@ -114,6 +114,16 @@
  */
 bool AMediaExtractor_advance(AMediaExtractor*);
 
+typedef enum {
+    AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC,
+    AMEDIAEXTRACTOR_SEEK_NEXT_SYNC,
+    AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC
+} SeekMode;
+
+/**
+ *
+ */
+media_status_t AMediaExtractor_seekTo(AMediaExtractor*, int64_t seekPosUs, SeekMode mode);
 
 /**
  * mapping of crypto scheme uuid to the scheme specific data for that scheme
@@ -146,7 +156,6 @@
     AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED = 2,
 };
 
-
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/include/ndk/NdkMediaMuxer.h b/include/ndk/NdkMediaMuxer.h
index db183e9..90d946c 100644
--- a/include/ndk/NdkMediaMuxer.h
+++ b/include/ndk/NdkMediaMuxer.h
@@ -56,18 +56,61 @@
  */
 media_status_t AMediaMuxer_delete(AMediaMuxer*);
 
-media_status_t AMediaMuxer_setLocation(AMediaMuxer*, float latitude, float longtitude);
+/**
+ * Set and store the geodata (latitude and longitude) in the output file.
+ * This method should be called before AMediaMuxer_start. The geodata is stored
+ * in udta box if the output format is AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4, and is
+ * ignored for other output formats.
+ * The geodata is stored according to ISO-6709 standard.
+ *
+ * Both values are specified in degrees.
+ * Latitude must be in the range [-90, 90].
+ * Longitude must be in the range [-180, 180].
+ */
+media_status_t AMediaMuxer_setLocation(AMediaMuxer*, float latitude, float longitude);
 
+/**
+ * Sets the orientation hint for output video playback.
+ * This method should be called before AMediaMuxer_start. Calling this
+ * method will not rotate the video frame when muxer is generating the file,
+ * but add a composition matrix containing the rotation angle in the output
+ * video if the output format is AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4, so that a
+ * video player can choose the proper orientation for playback.
+ * Note that some video players may choose to ignore the composition matrix
+ * during playback.
+ * The angle is specified in degrees, clockwise.
+ * The supported angles are 0, 90, 180, and 270 degrees.
+ */
 media_status_t AMediaMuxer_setOrientationHint(AMediaMuxer*, int degrees);
 
+/**
+ * Adds a track with the specified format.
+ * Returns the index of the new track or a negative value in case of failure,
+ * which can be interpreted as a media_status_t.
+ */
 ssize_t AMediaMuxer_addTrack(AMediaMuxer*, const AMediaFormat* format);
 
+/**
+ * Start the muxer. Should be called after AMediaMuxer_addTrack and
+ * before AMediaMuxer_writeSampleData.
+ */
 media_status_t AMediaMuxer_start(AMediaMuxer*);
 
+/**
+ * Stops the muxer.
+ * Once the muxer stops, it can not be restarted.
+ */
 media_status_t AMediaMuxer_stop(AMediaMuxer*);
 
+/**
+ * Writes an encoded sample into the muxer.
+ * The application needs to make sure that the samples are written into
+ * the right tracks. Also, it needs to make sure the samples for each track
+ * are written in chronological order (e.g. in the order they are provided
+ * by the encoder.)
+ */
 media_status_t AMediaMuxer_writeSampleData(AMediaMuxer *muxer,
-        size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo &info);
+        size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo *info);
 
 #ifdef __cplusplus
 } // extern "C"
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 3901e79..5116d1e 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <audio_utils/minifloat.h>
 #include <utils/threads.h>
 #include <utils/Log.h>
 #include <utils/RefBase.h>
@@ -110,11 +111,8 @@
                 // force to 32-bit.  The client and server may have different typedefs for size_t.
                 uint32_t    mMinimum;       // server wakes up client if available >= mMinimum
 
-                // Channel volumes are fixed point U4.12, so 0x1000 means 1.0.
-                // Left channel is in [0:15], right channel is in [16:31].
-                // Always read and write the combined pair atomically.
-                // For AudioTrack only, not used by AudioRecord.
-                uint32_t    mVolumeLR;
+                // Stereo gains for AudioTrack only, not used by AudioRecord.
+                gain_minifloat_packed_t mVolumeLR;
 
                 uint32_t    mSampleRate;    // AudioTrack only: client's requested sample rate in Hz
                                             // or 0 == default. Write-only client, read-only server.
@@ -285,8 +283,8 @@
         mCblk->mSendLevel = uint16_t(sendLevel * 0x1000);
     }
 
-    // caller must limit to 0 <= volumeLR <= 0x10001000
-    void        setVolumeLR(uint32_t volumeLR) {
+    // set stereo gains
+    void        setVolumeLR(gain_minifloat_packed_t volumeLR) {
         mCblk->mVolumeLR = volumeLR;
     }
 
@@ -405,7 +403,7 @@
     // return value of these methods must be validated by the caller
     uint32_t    getSampleRate() const { return mCblk->mSampleRate; }
     uint16_t    getSendLevel_U4_12() const { return mCblk->mSendLevel; }
-    uint32_t    getVolumeLR() const { return mCblk->mVolumeLR; }
+    gain_minifloat_packed_t getVolumeLR() const { return mCblk->mVolumeLR; }
 
     // estimated total number of filled frames available to server to read,
     // which may include non-contiguous frames
diff --git a/media/img_utils/Android.mk b/media/img_utils/Android.mk
new file mode 100644
index 0000000..1cd00bd
--- /dev/null
+++ b/media/img_utils/Android.mk
@@ -0,0 +1,15 @@
+# Copyright 2014 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 $(call all-subdir-makefiles)
diff --git a/media/img_utils/include/img_utils/ByteArrayOutput.h b/media/img_utils/include/img_utils/ByteArrayOutput.h
new file mode 100644
index 0000000..ba73977
--- /dev/null
+++ b/media/img_utils/include/img_utils/ByteArrayOutput.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_BYTE_ARRAY_OUTPUT_H
+#define IMG_UTILS_BYTE_ARRAY_OUTPUT_H
+
+#include <img_utils/Output.h>
+
+#include <utils/Errors.h>
+#include <utils/Vector.h>
+
+#include <cutils/compiler.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * Utility class that accumulates written bytes into a buffer.
+ */
+class ANDROID_API ByteArrayOutput : public Output {
+    public:
+
+        ByteArrayOutput();
+
+        virtual ~ByteArrayOutput();
+
+        /**
+         * Open this ByteArrayOutput.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t open();
+
+        /**
+         * Write bytes from the given buffer.  The number of bytes given in the count
+         * argument will be written.  Bytes will be written from the given buffer starting
+         * at the index given in the offset argument.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
+
+        /**
+         * Close this ByteArrayOutput.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t close();
+
+        /**
+         * Get current size of the array of bytes written.
+         */
+        virtual size_t getSize() const;
+
+        /**
+         * Get pointer to array of bytes written.  It is not valid to use this pointer if
+         * open, write, or close is called after this method.
+         */
+        virtual const uint8_t* getArray() const;
+
+    protected:
+        Vector<uint8_t> mByteArray;
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_BYTE_ARRAY_OUTPUT_H*/
diff --git a/media/img_utils/include/img_utils/DngUtils.h b/media/img_utils/include/img_utils/DngUtils.h
new file mode 100644
index 0000000..4389b02
--- /dev/null
+++ b/media/img_utils/include/img_utils/DngUtils.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_DNG_UTILS_H
+#define IMG_UTILS_DNG_UTILS_H
+
+#include <img_utils/ByteArrayOutput.h>
+#include <img_utils/EndianUtils.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+#include <cutils/compiler.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+#define NELEMS(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+/**
+ * Utility class for building values for the OpcodeList tags specified
+ * in the Adobe DNG 1.4 spec.
+ */
+class ANDROID_API OpcodeListBuilder : public LightRefBase<OpcodeListBuilder> {
+    public:
+        enum CfaLayout {
+            CFA_RGGB = 0,
+            CFA_GRBG,
+            CFA_GBRG,
+            CFA_BGGR,
+        };
+
+        OpcodeListBuilder();
+        virtual ~OpcodeListBuilder();
+
+        /**
+         * Get the total size of this opcode list in bytes.
+         */
+        virtual size_t getSize() const;
+
+        /**
+         * Get the number of opcodes defined in this list.
+         */
+        virtual uint32_t getCount() const;
+
+        /**
+         * Write the opcode list into the given buffer.  This buffer
+         * must be able to hold at least as many elements as returned
+         * by calling the getSize() method.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t buildOpList(/*out*/ uint8_t* buf) const;
+
+        /**
+         * Add GainMap opcode(s) for the given metadata parameters.  The given
+         * CFA layout must match the layout of the shading map passed into the
+         * lensShadingMap parameter.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t addGainMapsForMetadata(uint32_t lsmWidth,
+                                                uint32_t lsmHeight,
+                                                uint32_t activeAreaTop,
+                                                uint32_t activeAreaLeft,
+                                                uint32_t activeAreaBottom,
+                                                uint32_t activeAreaRight,
+                                                CfaLayout cfa,
+                                                const float* lensShadingMap);
+
+
+        /**
+         * Add a GainMap opcode with the given fields.  The mapGains array
+         * must have mapPointsV * mapPointsH * mapPlanes elements.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t addGainMap(uint32_t top,
+                                    uint32_t left,
+                                    uint32_t bottom,
+                                    uint32_t right,
+                                    uint32_t plane,
+                                    uint32_t planes,
+                                    uint32_t rowPitch,
+                                    uint32_t colPitch,
+                                    uint32_t mapPointsV,
+                                    uint32_t mapPointsH,
+                                    double mapSpacingV,
+                                    double mapSpacingH,
+                                    double mapOriginV,
+                                    double mapOriginH,
+                                    uint32_t mapPlanes,
+                                    const float* mapGains);
+
+        // TODO: Add other Opcode methods
+    protected:
+        static const uint32_t FLAG_OPTIONAL = 0x1u;
+        static const uint32_t FLAG_OPTIONAL_FOR_PREVIEW = 0x2u;
+
+        enum {
+            GAIN_MAP_ID = 9,
+            LSM_R_IND = 0,
+            LSM_GE_IND = 1,
+            LSM_GO_IND = 2,
+            LSM_B_IND = 3,
+        };
+
+        uint32_t mCount;
+        ByteArrayOutput mOpList;
+        EndianOutput mEndianOut;
+
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_DNG_UTILS_H*/
diff --git a/media/img_utils/include/img_utils/EndianUtils.h b/media/img_utils/include/img_utils/EndianUtils.h
new file mode 100644
index 0000000..e99be1a
--- /dev/null
+++ b/media/img_utils/include/img_utils/EndianUtils.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_ENDIAN_UTILS
+#define IMG_UTILS_ENDIAN_UTILS
+
+#include <img_utils/Output.h>
+
+#include <cutils/compiler.h>
+#include <utils/Errors.h>
+#include <stdint.h>
+#include <endian.h>
+#include <assert.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * Endianness types supported.
+ */
+enum ANDROID_API Endianness {
+    UNDEFINED_ENDIAN, // Default endianness will be used.
+    BIG,
+    LITTLE
+};
+
+/**
+ * Convert from the native device endianness to big endian.
+ */
+template<typename T>
+T convertToBigEndian(T in);
+
+/**
+ * Convert from the native device endianness to little endian.
+ */
+template<typename T>
+T convertToLittleEndian(T in);
+
+/**
+ * A utility class for writing to an Output with the given endianness.
+ */
+class ANDROID_API EndianOutput : public Output {
+    public:
+        /**
+         * Wrap the given Output.  Calling write methods will result in
+         * writes to this output.
+         */
+        EndianOutput(Output* out, Endianness end=LITTLE);
+
+        virtual ~EndianOutput();
+
+        /**
+         * Call open on the wrapped output.
+         */
+        virtual status_t open();
+
+        /**
+         * Call close on the wrapped output.
+         */
+        virtual status_t close();
+
+        /**
+         * Set the endianness to use when writing.
+         */
+        virtual void setEndianness(Endianness end);
+
+        /**
+         * Get the currently configured endianness.
+         */
+        virtual Endianness getEndianness() const;
+
+        /**
+         * Get the current number of bytes written by this EndianOutput.
+         */
+        virtual uint32_t getCurrentOffset() const;
+
+
+        // TODO: switch write methods to uint32_t instead of size_t,
+        // the max size of a TIFF files is bounded
+
+        /**
+         * The following methods will write elements from given input buffer to the output.
+         * Count elements in the buffer will be written with the endianness set for this
+         * EndianOutput.  If the given offset is greater than zero, that many elements will
+         * be skipped in the buffer before writing.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
+
+        virtual status_t write(const int8_t* buf, size_t offset, size_t count);
+
+        virtual status_t write(const uint16_t* buf, size_t offset, size_t count);
+
+        virtual status_t write(const int16_t* buf, size_t offset, size_t count);
+
+        virtual status_t write(const uint32_t* buf, size_t offset, size_t count);
+
+        virtual status_t write(const int32_t* buf, size_t offset, size_t count);
+
+        virtual status_t write(const uint64_t* buf, size_t offset, size_t count);
+
+        virtual status_t write(const int64_t* buf, size_t offset, size_t count);
+
+        virtual status_t write(const float* buf, size_t offset, size_t count);
+
+        virtual status_t write(const double* buf, size_t offset, size_t count);
+
+    protected:
+        template<typename T>
+        inline status_t writeHelper(const T* buf, size_t offset, size_t count);
+
+        uint32_t mOffset;
+        Output* mOutput;
+        Endianness mEndian;
+};
+
+template<typename T>
+inline status_t EndianOutput::writeHelper(const T* buf, size_t offset, size_t count) {
+    assert(offset <= count);
+    status_t res = OK;
+    size_t size = sizeof(T);
+    switch(mEndian) {
+        case BIG: {
+            for (size_t i = offset; i < count; ++i) {
+                T tmp = convertToBigEndian<T>(buf[offset + i]);
+                if ((res = mOutput->write(reinterpret_cast<uint8_t*>(&tmp), 0, size))
+                        != OK) {
+                    return res;
+                }
+                mOffset += size;
+            }
+            break;
+        }
+        case LITTLE: {
+            for (size_t i = offset; i < count; ++i) {
+                T tmp = convertToLittleEndian<T>(buf[offset + i]);
+                if ((res = mOutput->write(reinterpret_cast<uint8_t*>(&tmp), 0, size))
+                        != OK) {
+                    return res;
+                }
+                mOffset += size;
+            }
+            break;
+        }
+        default: {
+            return BAD_VALUE;
+        }
+    }
+    return res;
+}
+
+template<>
+inline uint8_t convertToBigEndian(uint8_t in) {
+    return in;
+}
+
+template<>
+inline int8_t convertToBigEndian(int8_t in) {
+    return in;
+}
+
+template<>
+inline uint16_t convertToBigEndian(uint16_t in) {
+    return htobe16(in);
+}
+
+template<>
+inline int16_t convertToBigEndian(int16_t in) {
+    return htobe16(in);
+}
+
+template<>
+inline uint32_t convertToBigEndian(uint32_t in) {
+    return htobe32(in);
+}
+
+template<>
+inline int32_t convertToBigEndian(int32_t in) {
+    return htobe32(in);
+}
+
+template<>
+inline uint64_t convertToBigEndian(uint64_t in) {
+    return htobe64(in);
+}
+
+template<>
+inline int64_t convertToBigEndian(int64_t in) {
+    return htobe64(in);
+}
+
+template<>
+inline uint8_t convertToLittleEndian(uint8_t in) {
+    return in;
+}
+
+template<>
+inline int8_t convertToLittleEndian(int8_t in) {
+    return in;
+}
+
+template<>
+inline uint16_t convertToLittleEndian(uint16_t in) {
+    return htole16(in);
+}
+
+template<>
+inline int16_t convertToLittleEndian(int16_t in) {
+    return htole16(in);
+}
+
+template<>
+inline uint32_t convertToLittleEndian(uint32_t in) {
+    return htole32(in);
+}
+
+template<>
+inline int32_t convertToLittleEndian(int32_t in) {
+    return htole32(in);
+}
+
+template<>
+inline uint64_t convertToLittleEndian(uint64_t in) {
+    return htole64(in);
+}
+
+template<>
+inline int64_t convertToLittleEndian(int64_t in) {
+    return htole64(in);
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_ENDIAN_UTILS*/
+
diff --git a/media/img_utils/include/img_utils/FileInput.h b/media/img_utils/include/img_utils/FileInput.h
new file mode 100644
index 0000000..d3c5ec1
--- /dev/null
+++ b/media/img_utils/include/img_utils/FileInput.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_FILE_INPUT_H
+#define IMG_UTILS_FILE_INPUT_H
+
+#include <img_utils/Input.h>
+
+#include <cutils/compiler.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <stdio.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * Utility class for reading from a file.
+ */
+class ANDROID_API FileInput : public Input {
+    public:
+        /**
+         * Create a file input for the given path.
+         */
+        FileInput(String8 path);
+
+        virtual ~FileInput();
+
+        /**
+         * Open a file descriptor to the path given in the constructor.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t open();
+
+        /**
+         * Read bytes from the file into the given buffer.  At most, the number
+         * of bytes given in the count argument will be read.  Bytes will be written
+         * into the given buffer starting at the index given in the offset argument.
+         *
+         * Returns the number of bytes read.  If an error has occurred, the value pointed
+         * to by the given status_t pointer will be set to a negative error code.
+         */
+        virtual size_t read(uint8_t* buf, size_t offset, size_t count, status_t* err);
+
+        /**
+         * Close the file descriptor to the path given in the constructor.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t close();
+    private:
+        FILE *mFp;
+        String8 mPath;
+        bool mOpen;
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+
+#endif /*IMG_UTILS_INPUT_H*/
diff --git a/media/img_utils/include/img_utils/FileOutput.h b/media/img_utils/include/img_utils/FileOutput.h
new file mode 100644
index 0000000..fd5be27
--- /dev/null
+++ b/media/img_utils/include/img_utils/FileOutput.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_FILE_OUTPUT_H
+#define IMG_UTILS_FILE_OUTPUT_H
+
+#include <img_utils/Output.h>
+#include <cutils/compiler.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <stdio.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+class ANDROID_API FileOutput : public Output {
+    public:
+        FileOutput(String8 path);
+        virtual ~FileOutput();
+        virtual status_t open();
+        virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
+        virtual status_t close();
+    private:
+        FILE *mFp;
+        String8 mPath;
+        bool mOpen;
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_FILE_OUTPUT_H*/
diff --git a/media/img_utils/include/img_utils/Input.h b/media/img_utils/include/img_utils/Input.h
new file mode 100644
index 0000000..2166601
--- /dev/null
+++ b/media/img_utils/include/img_utils/Input.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_INPUT_H
+#define IMG_UTILS_INPUT_H
+
+#include <cutils/compiler.h>
+#include <utils/Errors.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * Utility class used as a source of bytes.
+ */
+class ANDROID_API Input {
+    public:
+        virtual ~Input();
+
+        /**
+         * Open this Input.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t open();
+
+        /**
+         * Read bytes into the given buffer.  At most, the number of bytes given in the
+         * count argument will be read.  Bytes will be written into the given buffer starting
+         * at the index given in the offset argument.
+         *
+         * Returns the number of bytes read.  If an error has occurred, the value pointed
+         * to by the given status_t pointer will be set to a negative error code.
+         */
+        virtual size_t read(uint8_t* buf, size_t offset, size_t count, status_t* err) = 0;
+
+        /**
+         * Close the Input.  It is not valid to call open on a previously closed Input.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t close();
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+
+#endif /*IMG_UTILS_INPUT_H*/
diff --git a/media/img_utils/include/img_utils/Orderable.h b/media/img_utils/include/img_utils/Orderable.h
new file mode 100644
index 0000000..87253a4
--- /dev/null
+++ b/media/img_utils/include/img_utils/Orderable.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_ORDERABLE
+#define IMG_UTILS_ORDERABLE
+
+#include <cutils/compiler.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+#define COMPARE_DEF(op) \
+inline bool operator op (const Orderable& orderable) const;
+
+/**
+ * Subclasses of Orderable can be compared and sorted.  This is
+ * intended to be used to create sorted arrays of TIFF entries
+ * and IFDs.
+ */
+class ANDROID_API Orderable  {
+    public:
+        virtual ~Orderable();
+
+        /**
+         * Comparison operatotors are based on the value returned
+         * from this method.
+         */
+        virtual uint32_t getComparableValue() const = 0;
+
+        COMPARE_DEF(>)
+        COMPARE_DEF(<)
+        COMPARE_DEF(>=)
+        COMPARE_DEF(<=)
+        COMPARE_DEF(==)
+        COMPARE_DEF(!=)
+};
+
+#undef COMPARE_DEF
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_ORDERABLE*/
diff --git a/media/img_utils/include/img_utils/Output.h b/media/img_utils/include/img_utils/Output.h
new file mode 100644
index 0000000..35fae23
--- /dev/null
+++ b/media/img_utils/include/img_utils/Output.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_OUTPUT_H
+#define IMG_UTILS_OUTPUT_H
+
+#include <cutils/compiler.h>
+#include <utils/Errors.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * Utility class used to output bytes.
+ */
+class ANDROID_API Output {
+    public:
+        virtual ~Output();
+
+        /**
+         * Open this Output.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t open();
+
+        /**
+         * Write bytes from the given buffer.  The number of bytes given in the count
+         * argument will be written.  Bytes will be written from the given buffer starting
+         * at the index given in the offset argument.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t write(const uint8_t* buf, size_t offset, size_t count) = 0;
+
+        /**
+         * Close this Output.  It is not valid to call open on a previously closed Output.
+         *
+         * Returns OK on success, or a negative error code.
+         */
+        virtual status_t close();
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_OUTPUT_H*/
diff --git a/media/img_utils/include/img_utils/Pair.h b/media/img_utils/include/img_utils/Pair.h
new file mode 100644
index 0000000..d651cac
--- /dev/null
+++ b/media/img_utils/include/img_utils/Pair.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_PAIR_H
+#define IMG_UTILS_PAIR_H
+
+#include <cutils/compiler.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * Generic pair utility class.  Nothing special here.
+ */
+template<typename F, typename S>
+class ANDROID_API Pair {
+    public:
+        F first;
+        S second;
+
+        Pair() {}
+
+        Pair(const Pair& o) : first(o.first), second(o.second) {}
+
+        Pair(const F& f, const S& s) : first(f), second(s) {}
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_PAIR_H*/
diff --git a/media/img_utils/include/img_utils/SortedEntryVector.h b/media/img_utils/include/img_utils/SortedEntryVector.h
new file mode 100644
index 0000000..f059a82
--- /dev/null
+++ b/media/img_utils/include/img_utils/SortedEntryVector.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_SORTED_ENTRY_VECTOR_H
+#define IMG_UTILS_SORTED_ENTRY_VECTOR_H
+
+#include <img_utils/TiffEntry.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/SortedVector.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * Subclass of SortedVector that has been extended to
+ * do comparisons/lookups based on the tag ID of the entries.
+ */
+class SortedEntryVector : public SortedVector<sp<TiffEntry> > {
+    public:
+        virtual ~SortedEntryVector();
+
+        /**
+         * Returns the index of the entry with the given tag ID, or
+         * -1 if none exists.
+         */
+        ssize_t indexOfTag(uint16_t tag) const;
+
+    protected:
+        /**
+         * Compare tag ID.
+         */
+        virtual int do_compare(const void* lhs, const void* rhs) const;
+};
+
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_SORTED_ENTRY_VECTOR_H*/
diff --git a/media/img_utils/include/img_utils/TagDefinitions.h b/media/img_utils/include/img_utils/TagDefinitions.h
new file mode 100644
index 0000000..9232e58
--- /dev/null
+++ b/media/img_utils/include/img_utils/TagDefinitions.h
@@ -0,0 +1,1139 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_TIFF_TAG_DEFINITION_H
+#define IMG_UTILS_TIFF_TAG_DEFINITION_H
+
+#include <img_utils/TiffEntry.h>
+#include <img_utils/Output.h>
+#include <img_utils/TiffHelpers.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * Tag definitions contain information about standard TIFF compatible tags.
+ */
+typedef struct TagDefinition {
+    // The specified tag ID.
+    uint16_t tagId;
+    // The default type for this tag.  This must be a valid TIFF type.
+    TagType defaultType;
+    // The default Image File Directory (IFD) for this tag.
+    uint32_t defaultIfd;
+    // The valid count for this tag, or 0 if the count is not fixed.
+    uint32_t fixedCount;
+    // The endianness of the tag value, or UNDEFINED_ENDIAN if there is no fixed endian
+    Endianness fixedEndian;
+} TagDefinition_t;
+
+/**
+ * Convenience defines for tag ids.
+ */
+enum {
+    TAG_RAWTOPREVIEWGAIN = 0xC7A8u,
+    TAG_NEWRAWIMAGEDIGEST = 0xC7A7u,
+    TAG_ORIGINALDEFAULTCROPSIZE = 0xC793u,
+    TAG_ORIGINALBESTQUALITYFINALSIZE = 0xC792u,
+    TAG_ORIGINALDEFAULTFINALSIZE = 0xC791u,
+    TAG_PROFILEHUESATMAPENCODING = 0xC7A3u,
+    TAG_PROFILELOOKTABLEENCODING = 0xC7A4u,
+    TAG_BASELINEEXPOSUREOFFSET = 0xC7A5u,
+    TAG_DEFAULTBLACKRENDER = 0xC7A6u,
+    TAG_DEFAULTUSERCROP = 0xC7B5u,
+    TAG_NOISEPROFILE = 0xC761u,
+    TAG_OPCODELIST3 = 0xC74Eu,
+    TAG_OPCODELIST2 = 0xC741u,
+    TAG_OPCODELIST1 = 0xC740u,
+    TAG_PROFILELOOKTABLEDATA = 0xC726u,
+    TAG_PROFILELOOKTABLEDIMS = 0xC725u,
+    TAG_ROWINTERLEAVEFACTOR = 0xC71Fu,
+    TAG_SUBTILEBLOCKSIZE = 0xC71Eu,
+    TAG_ORIGINALRAWFILEDIGEST = 0xC71Du,
+    TAG_RAWIMAGEDIGEST = 0xC71Cu,
+    TAG_PREVIEWDATETIME = 0xC71Bu,
+    TAG_PREVIEWCOLORSPACE = 0xC71Au,
+    TAG_PREVIEWSETTINGSDIGEST = 0xC719u,
+    TAG_PREVIEWSETTINGSNAME = 0xC718u,
+    TAG_PREVIEWAPPLICATIONVERSION = 0xC717u,
+    TAG_PREVIEWAPPLICATIONNAME = 0xC716u,
+    TAG_FORWARDMATRIX2 = 0xC715u,
+    TAG_FORWARDMATRIX1 = 0xC714u,
+    TAG_PROFILECOPYRIGHT = 0xC6FEu,
+    TAG_PROFILEEMBEDPOLICY = 0xC6FDu,
+    TAG_PROFILETONECURVE = 0xC6FCu,
+    TAG_PROFILEHUESATMAPDATA2 = 0xC6FBu,
+    TAG_PROFILEHUESATMAPDATA1 = 0xC6FAu,
+    TAG_PROFILEHUESATMAPDIMS = 0xC6F9u,
+    TAG_PROFILENAME = 0xC6F8u,
+    TAG_NOISEREDUCTIONAPPLIED = 0xC6F7u,
+    TAG_ASSHOTPROFILENAME = 0xC6F6u,
+    TAG_EXTRACAMERAPROFILES = 0xC6F5u,
+    TAG_PROFILECALIBRATIONSIGNATURE = 0xC6F4u,
+    TAG_CAMERACALIBRATIONSIGNATURE = 0xC6F3u,
+    TAG_COLORIMETRICREFERENCE = 0xC6BFu,
+    TAG_CURRENTPREPROFILEMATRIX = 0xC692u,
+    TAG_CURRENTICCPROFILE = 0xC691u,
+    TAG_ASSHOTPREPROFILEMATRIX = 0xC690u,
+    TAG_ASSHOTICCPROFILE = 0xC68Fu,
+    TAG_MASKEDAREAS = 0xC68Eu,
+    TAG_ACTIVEAREA = 0xC68Du,
+    TAG_ORIGINALRAWFILEDATA = 0xC68Cu,
+    TAG_ORIGINALRAWFILENAME = 0xC68Bu,
+    TAG_RAWDATAUNIQUEID = 0xC65Du,
+    TAG_MAKERNOTESAFETY = 0xC635u,
+    TAG_DNGPRIVATEDATA = 0xC634u,
+    TAG_SHADOWSCALE = 0xC633u,
+    TAG_ANTIALIASSTRENGTH = 0xC632u,
+    TAG_CHROMABLURRADIUS = 0xC631u,
+    TAG_LENSINFO = 0xC630u,
+    TAG_CAMERASERIALNUMBER = 0xC62Fu,
+    TAG_LINEARRESPONSELIMIT = 0xC62Eu,
+    TAG_BAYERGREENSPLIT = 0xC62Du,
+    TAG_BASELINESHARPNESS = 0xC62Cu,
+    TAG_BASELINENOISE = 0xC62Bu,
+    TAG_BASELINEEXPOSURE = 0xC62Au,
+    TAG_ASSHOTWHITEXY = 0xC629u,
+    TAG_ASSHOTNEUTRAL = 0xC628u,
+    TAG_ANALOGBALANCE = 0xC627u,
+    TAG_REDUCTIONMATRIX2 = 0xC626u,
+    TAG_REDUCTIONMATRIX1 = 0xC625u,
+    TAG_CAMERACALIBRATION2 = 0xC624u,
+    TAG_CAMERACALIBRATION1 = 0xC623u,
+    TAG_COLORMATRIX2 = 0xC622u,
+    TAG_COLORMATRIX1 = 0xC621u,
+    TAG_CALIBRATIONILLUMINANT2 = 0xC65Bu,
+    TAG_CALIBRATIONILLUMINANT1 = 0xC65Au,
+    TAG_DEFAULTCROPSIZE = 0xC620u,
+    TAG_DEFAULTCROPORIGIN = 0xC61Fu,
+    TAG_BESTQUALITYSCALE = 0xC65Cu,
+    TAG_DEFAULTSCALE = 0xC61Eu,
+    TAG_WHITELEVEL = 0xC61Du,
+    TAG_BLACKLEVELDELTAV = 0xC61Cu,
+    TAG_BLACKLEVELDELTAH = 0xC61Bu,
+    TAG_BLACKLEVEL = 0xC61Au,
+    TAG_BLACKLEVELREPEATDIM = 0xC619u,
+    TAG_LINEARIZATIONTABLE = 0xC618u,
+    TAG_CFALAYOUT = 0xC617u,
+    TAG_CFAPLANECOLOR = 0xC616u,
+    TAG_LOCALIZEDCAMERAMODEL = 0xC615u,
+    TAG_UNIQUECAMERAMODEL = 0xC614u,
+    TAG_DNGBACKWARDVERSION = 0xC613u,
+    TAG_DNGVERSION = 0xC612u,
+    TAG_SUBFILETYPE = 0x00FFu,
+    TAG_YRESOLUTION = 0x011Bu,
+    TAG_XRESOLUTION = 0x011Au,
+    TAG_THRESHHOLDING = 0x0107u,
+    TAG_STRIPOFFSETS = 0x0111u,
+    TAG_STRIPBYTECOUNTS = 0x0117u,
+    TAG_SOFTWARE = 0x0131u,
+    TAG_SAMPLESPERPIXEL = 0x0115u,
+    TAG_ROWSPERSTRIP = 0x0116u,
+    TAG_RESOLUTIONUNIT = 0x0128u,
+    TAG_PLANARCONFIGURATION = 0x011Cu,
+    TAG_PHOTOMETRICINTERPRETATION = 0x0106u,
+    TAG_ORIENTATION = 0x0112u,
+    TAG_NEWSUBFILETYPE = 0x00FEu,
+    TAG_MODEL = 0x0110u,
+    TAG_MINSAMPLEVALUE = 0x0118u,
+    TAG_MAXSAMPLEVALUE = 0x0119u,
+    TAG_MAKE = 0x010Fu,
+    TAG_IMAGEWIDTH = 0x0100u,
+    TAG_IMAGELENGTH = 0x0101u,
+    TAG_IMAGEDESCRIPTION = 0x010Eu,
+    TAG_HOSTCOMPUTER = 0x013Cu,
+    TAG_GRAYRESPONSEUNIT = 0x0122u,
+    TAG_GRAYRESPONSECURVE = 0x0123u,
+    TAG_FREEOFFSETS = 0x0120u,
+    TAG_FREEBYTECOUNTS = 0x0121u,
+    TAG_FILLORDER = 0x010Au,
+    TAG_EXTRASAMPLES = 0x0152u,
+    TAG_DATETIME = 0x0132u,
+    TAG_COPYRIGHT = 0x8298u,
+    TAG_COMPRESSION = 0x0103u,
+    TAG_COLORMAP = 0x0140u,
+    TAG_CELLWIDTH = 0x0108u,
+    TAG_CELLLENGTH = 0x0109u,
+    TAG_BITSPERSAMPLE = 0x0102u,
+    TAG_ARTIST = 0x013Bu,
+    TAG_EXIFVERSION = 0x9000u,
+    TAG_CFAREPEATPATTERNDIM = 0x828Du,
+    TAG_CFAPATTERN = 0x828Eu,
+    TAG_SUBIFDS = 0x014Au,
+};
+
+/**
+ * TIFF_EP_TAG_DEFINITIONS contains tags defined in the TIFF EP spec
+ */
+const TagDefinition_t TIFF_EP_TAG_DEFINITIONS[] =  {
+    { // PhotometricInterpretation
+        0x0106u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // SubIfds
+        0x014Au,
+        LONG,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // CFAPattern
+        0x828Eu,
+        BYTE,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // CFARepeatPatternDim
+        0x828Du,
+        SHORT,
+        IFD_0,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    /*TODO: Remaining TIFF EP tags*/
+};
+
+/**
+ * EXIF_2_3_TAG_DEFINITIONS contains tags defined in the Jeita EXIF 2.3 spec
+ */
+const TagDefinition_t EXIF_2_3_TAG_DEFINITIONS[] = {
+    { // ExifVersion
+        0x9000u,
+        UNDEFINED,
+        IFD_0,
+        4,
+        UNDEFINED_ENDIAN
+    },
+    /*TODO: Remaining EXIF 2.3 tags*/
+};
+
+/**
+ * TIFF_6_TAG_DEFINITIONS contains tags defined in the TIFF 6.0 spec
+ */
+const TagDefinition_t TIFF_6_TAG_DEFINITIONS[] = {
+    { // SubFileType
+        0x00FFu,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // Artist
+        0x013Bu,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // BitsPerSample
+        0x0102u,
+        SHORT,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // CellLength
+        0x0109u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // CellWidth
+        0x0108u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // ColorMap
+        0x0140u,
+        SHORT,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // Compression
+        0x0103u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // Copyright
+        0x8298u,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // DateTime
+        0x0132u,
+        ASCII,
+        IFD_0,
+        20,
+        UNDEFINED_ENDIAN
+    },
+    { // ExtraSamples
+        0x0152u,
+        SHORT,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // FillOrder
+        0x010Au,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // FreeByteCounts
+        0x0121u,
+        LONG,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // FreeOffsets
+        0x0120u,
+        LONG,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // GrayResponseCurve
+        0x0123u,
+        SHORT,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // GrayResponseUnit
+        0x0122u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // HostComputer
+        0x013Cu,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ImageDescription
+        0x010Eu,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ImageLength
+        0x0101u,
+        LONG,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // ImageWidth
+        0x0100u,
+        LONG,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // Make
+        0x010Fu,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // MaxSampleValue
+        0x0119u,
+        SHORT,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // MinSampleValue
+        0x0118u,
+        SHORT,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // Model
+        0x0110u,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // NewSubfileType
+        0x00FEu,
+        LONG,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // Orientation
+        0x0112u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // PhotoMetricInterpretation
+        0x0106u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // PlanarConfiguration
+        0x011Cu,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // ResolutionUnit
+        0x0128u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // RowsPerStrip
+        0x0116u,
+        LONG,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // SamplesPerPixel
+        0x0115u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // Software
+        0x0131u,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // StripByteCounts
+        0x0117u,
+        LONG,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // StripOffsets
+        0x0111u,
+        LONG,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // SubfileType
+        0x00FFu,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // Threshholding
+        0x0107u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // XResolution
+        0x011Au,
+        RATIONAL,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // YResolution
+        0x011Bu,
+        RATIONAL,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // YResolution
+        0x011Bu,
+        RATIONAL,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    }
+};
+
+/**
+ * DNG_TAG_DEFINITIONS contains tags defined in the DNG 1.4 spec
+ */
+const TagDefinition_t DNG_TAG_DEFINITIONS[] = {
+    { // DNGVersion
+        0xC612u,
+        BYTE,
+        IFD_0,
+        4,
+        UNDEFINED_ENDIAN
+    },
+    { // DNGBackwardVersion
+        0xC613u,
+        BYTE,
+        IFD_0,
+        4,
+        UNDEFINED_ENDIAN
+    },
+    { // UniqueCameraModel
+        0xC614u,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // LocalizedCameraModel
+        0xC615u,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // CFAPlaneColor
+        0xC616u,
+        BYTE,
+        RAW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // CFALayout
+        0xC617u,
+        SHORT,
+        RAW_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // LinearizationTable
+        0xC618u,
+        SHORT,
+        RAW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // BlackLevelRepeatDim
+        0xC619u,
+        SHORT,
+        RAW_IFD,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // BlackLevel
+        0xC61Au,
+        LONG,
+        RAW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // BlackLevelDeltaH
+        0xC61Bu,
+        SRATIONAL,
+        RAW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // BlackLevelDeltaV
+        0xC61Cu,
+        SRATIONAL,
+        RAW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // WhiteLevel
+        0xC61Du,
+        LONG,
+        RAW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // DefaultScale
+        0xC61Eu,
+        RATIONAL,
+        RAW_IFD,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // BestQualityScale
+        0xC65Cu,
+        RATIONAL,
+        RAW_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // DefaultCropOrigin
+        0xC61Fu,
+        LONG,
+        RAW_IFD,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // DefaultCropSize
+        0xC620u,
+        LONG,
+        RAW_IFD,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // CalibrationIlluminant1
+        0xC65Au,
+        SHORT,
+        PROFILE_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // CalibrationIlluminant2
+        0xC65Bu,
+        SHORT,
+        PROFILE_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // ColorMatrix1
+        0xC621u,
+        SRATIONAL,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ColorMatrix2
+        0xC622u,
+        SRATIONAL,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // CameraCalibration1
+        0xC623u,
+        SRATIONAL,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // CameraCalibration2
+        0xC624u,
+        SRATIONAL,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ReductionMatrix1
+        0xC625u,
+        SRATIONAL,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ReductionMatrix2
+        0xC626u,
+        SRATIONAL,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // AnalogBalance
+        0xC627u,
+        RATIONAL,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // AsShotNeutral
+        0xC628u,
+        RATIONAL,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // AsShotWhiteXY
+        0xC629u,
+        RATIONAL,
+        IFD_0,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // BaselineExposure
+        0xC62Au,
+        SRATIONAL,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // BaselineNoise
+        0xC62Bu,
+        RATIONAL,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // BaselineSharpness
+        0xC62Cu,
+        RATIONAL,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // BayerGreenSplit
+        0xC62Du,
+        LONG,
+        RAW_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // LinearResponseLimit
+        0xC62Eu,
+        RATIONAL,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // CameraSerialNumber
+        0xC62Fu,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // LensInfo
+        0xC630u,
+        RATIONAL,
+        IFD_0,
+        4,
+        UNDEFINED_ENDIAN
+    },
+    { // ChromaBlurRadius
+        0xC631u,
+        RATIONAL,
+        RAW_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // AntiAliasStrength
+        0xC632u,
+        RATIONAL,
+        RAW_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // ShadowScale
+        0xC633u,
+        RATIONAL,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // DNGPrivateData
+        0xC634u,
+        BYTE,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // MakerNoteSafety
+        0xC635u,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // RawDataUniqueID
+        0xC65Du,
+        BYTE,
+        IFD_0,
+        16,
+        UNDEFINED_ENDIAN
+    },
+    { // OriginalRawFileName
+        0xC68Bu,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // OriginalRawFileData
+        0xC68Cu,
+        UNDEFINED,
+        IFD_0,
+        0,
+        BIG
+    },
+    { // ActiveArea
+        0xC68Du,
+        LONG,
+        RAW_IFD,
+        4,
+        UNDEFINED_ENDIAN
+    },
+    { // MaskedAreas
+        0xC68Eu,
+        LONG,
+        RAW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // AsShotICCProfile
+        0xC68Fu,
+        UNDEFINED,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // AsShotPreProfileMatrix
+        0xC690u,
+        SRATIONAL,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // CurrentICCProfile
+        0xC691u,
+        UNDEFINED,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // CurrentICCProfile
+        0xC691u,
+        UNDEFINED,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // CurrentPreProfileMatrix
+        0xC692u,
+        SRATIONAL,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ColorimetricReference
+        0xC6BFu,
+        SHORT,
+        IFD_0,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // CameraCalibrationSignature
+        0xC6F3u,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileCalibrationSignature
+        0xC6F4u,
+        ASCII,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ExtraCameraProfiles
+        0xC6F5u,
+        LONG,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // AsShotProfileName
+        0xC6F6u,
+        ASCII,
+        IFD_0,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // NoiseReductionApplied
+        0xC6F7u,
+        RATIONAL,
+        RAW_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileName
+        0xC6F8u,
+        ASCII,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileHueSatMapDims
+        0xC6F9u,
+        LONG,
+        PROFILE_IFD,
+        3,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileHueSatMapData1
+        0xC6FAu,
+        FLOAT,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileHueSatMapData2
+        0xC6FBu,
+        FLOAT,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileToneCurve
+        0xC6FCu,
+        FLOAT,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileEmbedPolicy
+        0xC6FDu,
+        LONG,
+        PROFILE_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileCopyright
+        0xC6FEu,
+        ASCII,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ForwardMatrix1
+        0xC714u,
+        SRATIONAL,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // ForwardMatrix2
+        0xC715u,
+        SRATIONAL,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // PreviewApplicationName
+        0xC716u,
+        ASCII,
+        PREVIEW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // PreviewApplicationVersion
+        0xC717u,
+        ASCII,
+        PREVIEW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // PreviewSettingsName
+        0xC718u,
+        ASCII,
+        PREVIEW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // PreviewSettingsDigest
+        0xC719u,
+        BYTE,
+        PREVIEW_IFD,
+        16,
+        UNDEFINED_ENDIAN
+    },
+    { // PreviewColorSpace
+        0xC71Au,
+        LONG,
+        PREVIEW_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // PreviewDateTime
+        0xC71Bu,
+        ASCII,
+        PREVIEW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // RawImageDigest
+        0xC71Cu,
+        BYTE,
+        IFD_0,
+        16,
+        UNDEFINED_ENDIAN
+    },
+    { // OriginalRawFileDigest
+        0xC71Du,
+        BYTE,
+        IFD_0,
+        16,
+        UNDEFINED_ENDIAN
+    },
+    { // SubTileBlockSize
+        0xC71Eu,
+        LONG,
+        RAW_IFD,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // RowInterleaveFactor
+        0xC71Fu,
+        LONG,
+        RAW_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileLookTableDims
+        0xC725u,
+        LONG,
+        PROFILE_IFD,
+        3,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileLookTableData
+        0xC726u,
+        FLOAT,
+        PROFILE_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // OpcodeList1
+        0xC740u,
+        UNDEFINED,
+        RAW_IFD,
+        0,
+        BIG
+    },
+    { // OpcodeList2
+        0xC741u,
+        UNDEFINED,
+        RAW_IFD,
+        0,
+        BIG
+    },
+    { // OpcodeList3
+        0xC74Eu,
+        UNDEFINED,
+        RAW_IFD,
+        0,
+        BIG
+    },
+    { // NoiseProfile
+        0xC761u,
+        DOUBLE,
+        RAW_IFD,
+        0,
+        UNDEFINED_ENDIAN
+    },
+    { // DefaultUserCrop
+        0xC7B5u,
+        RATIONAL,
+        RAW_IFD,
+        4,
+        UNDEFINED_ENDIAN
+    },
+    { // DefaultBlackRender
+        0xC7A6u,
+        LONG,
+        PROFILE_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // BaselineExposureOffset
+        0xC7A5u,
+        RATIONAL,
+        PROFILE_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileLookTableEncoding
+        0xC7A4u,
+        LONG,
+        PROFILE_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // ProfileHueSatMapEncoding
+        0xC7A3u,
+        LONG,
+        PROFILE_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+    { // OriginalDefaultFinalSize
+        0xC791u,
+        LONG,
+        IFD_0,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // OriginalBestQualityFinalSize
+        0xC792u,
+        LONG,
+        IFD_0,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // OriginalDefaultCropSize
+        0xC793u,
+        LONG,
+        IFD_0,
+        2,
+        UNDEFINED_ENDIAN
+    },
+    { // NewRawImageDigest
+        0xC7A7u,
+        BYTE,
+        IFD_0,
+        16,
+        UNDEFINED_ENDIAN
+    },
+    { // RawToPreviewGain
+        0xC7A8u,
+        DOUBLE,
+        PREVIEW_IFD,
+        1,
+        UNDEFINED_ENDIAN
+    },
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_TIFF_TAG_DEFINITION_H*/
diff --git a/media/img_utils/include/img_utils/TiffEntry.h b/media/img_utils/include/img_utils/TiffEntry.h
new file mode 100644
index 0000000..cd01640
--- /dev/null
+++ b/media/img_utils/include/img_utils/TiffEntry.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_TIFF_ENTRY
+#define IMG_UTILS_TIFF_ENTRY
+
+#include <img_utils/TiffWritable.h>
+#include <img_utils/TiffHelpers.h>
+#include <img_utils/EndianUtils.h>
+
+#include <cutils/compiler.h>
+#include <utils/String8.h>
+#include <utils/Errors.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+#define COMPARE_DEF(op) \
+inline bool operator op (const TiffEntry& entry) const;
+
+/**
+ * This class holds a single TIFF IFD entry.
+ */
+class ANDROID_API TiffEntry : public TiffWritable {
+    public:
+        // TODO: Copy constructor/equals here.
+        virtual ~TiffEntry();
+
+        /**
+         * Write the 12-byte IFD entry to the output. The given offset will be
+         * set as the tag value if the size of the tag value exceeds the max
+         * size for the TIFF Value field (4 bytes), and should be word aligned.
+         *
+         * Returns OK on success, or a negative error code on failure.
+         */
+        virtual status_t writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const = 0;
+
+        /**
+         * Get the count set for this entry. This corresponds to the TIFF Count
+         * field.
+         */
+        virtual uint32_t getCount() const = 0;
+
+        /**
+         * Get the tag id set for this entry. This corresponds to the TIFF Tag
+         * field.
+         */
+        virtual uint16_t getTag() const = 0;
+
+        /**
+         * Get the type set for this entry.  This corresponds to the TIFF Type
+         * field.
+         */
+        virtual TagType getType() const = 0;
+
+        /**
+         * Get the defined endianness for this entry.  If this is defined,
+         * the tag value will be written with the given byte order.
+         */
+        virtual Endianness getEndianness() const = 0;
+
+        /**
+         * Get the value for this entry.  This corresponds to the TIFF Value
+         * field.
+         *
+         * Returns NULL if the value is NULL, or if the type used does not
+         * match the type of this tag.
+         */
+        template<typename T>
+        const T* getData() const;
+
+        String8 toString() const;
+
+        /**
+         * Force the type used here to be a valid TIFF type.
+         *
+         * Returns NULL if the given value is NULL, or if the type given does
+         * not match the type of the value given.
+         */
+        template<typename T>
+        static const T* forceValidType(TagType type, const T* value);
+
+        virtual const void* getDataHelper() const = 0;
+
+        COMPARE_DEF(>)
+        COMPARE_DEF(<)
+
+        protected:
+            enum {
+                MAX_PRINT_STRING_LENGTH = 256
+            };
+};
+
+#define COMPARE(op) \
+bool TiffEntry::operator op (const TiffEntry& entry) const { \
+    return getComparableValue() op entry.getComparableValue(); \
+}
+
+COMPARE(>)
+COMPARE(<)
+
+
+template<typename T>
+const T* TiffEntry::getData() const {
+    const T* value = reinterpret_cast<const T*>(getDataHelper());
+    return forceValidType<T>(getType(), value);
+}
+
+#undef COMPARE
+#undef COMPARE_DEF
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_TIFF_ENTRY*/
diff --git a/media/img_utils/include/img_utils/TiffEntryImpl.h b/media/img_utils/include/img_utils/TiffEntryImpl.h
new file mode 100644
index 0000000..cbe0e9a
--- /dev/null
+++ b/media/img_utils/include/img_utils/TiffEntryImpl.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_TIFF_ENTRY_IMPL
+#define IMG_UTILS_TIFF_ENTRY_IMPL
+
+#include <img_utils/TiffEntry.h>
+#include <img_utils/TiffHelpers.h>
+#include <img_utils/Output.h>
+#include <img_utils/EndianUtils.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+template<typename T>
+class TiffEntryImpl : public TiffEntry {
+    public:
+        // TODO: Copy constructor/equals here.
+        TiffEntryImpl(uint16_t tag, TagType type, uint32_t count, Endianness end, const T* data);
+        virtual ~TiffEntryImpl();
+
+        status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const;
+        status_t writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const;
+
+        uint32_t getCount() const;
+        uint16_t getTag() const;
+        TagType getType() const;
+        Endianness getEndianness() const;
+        size_t getSize() const;
+        uint32_t getComparableValue() const;
+
+    protected:
+        const void* getDataHelper() const;
+        uint32_t getActualSize() const;
+
+        uint16_t mTag;
+        uint16_t mType;
+        uint32_t mCount;
+        Endianness mEnd;
+        T* mData;
+
+};
+
+template<typename T>
+TiffEntryImpl<T>::TiffEntryImpl(uint16_t tag, TagType type, uint32_t count, Endianness end,
+        const T* data)
+        : mTag(tag), mType(static_cast<uint16_t>(type)), mCount(count), mEnd(end) {
+    count = (type == RATIONAL || type == SRATIONAL) ? count * 2 : count;
+    mData = new T[count]();
+    for (uint32_t i = 0; i < count; ++i) {
+        mData[i] = data[i];
+    }
+}
+
+template<typename T>
+TiffEntryImpl<T>::~TiffEntryImpl() {
+    if (mData) {
+        delete[] mData;
+    }
+}
+
+template<typename T>
+uint32_t TiffEntryImpl<T>::getCount() const {
+    return mCount;
+}
+
+template<typename T>
+uint16_t TiffEntryImpl<T>::getTag() const {
+    return mTag;
+}
+
+template<typename T>
+TagType TiffEntryImpl<T>::getType() const {
+    return static_cast<TagType>(mType);
+}
+
+template<typename T>
+const void* TiffEntryImpl<T>::getDataHelper() const {
+    return reinterpret_cast<const void*>(mData);
+}
+
+template<typename T>
+size_t TiffEntryImpl<T>::getSize() const {
+    uint32_t total = getActualSize();
+    WORD_ALIGN(total)
+    return (total <= OFFSET_SIZE) ? 0 : total;
+}
+
+template<typename T>
+uint32_t TiffEntryImpl<T>::getActualSize() const {
+    uint32_t total = sizeof(T) * mCount;
+    if (getType() == RATIONAL || getType() == SRATIONAL) {
+        // 2 ints stored for each rational, multiply by 2
+        total <<= 1;
+    }
+    return total;
+}
+
+template<typename T>
+Endianness TiffEntryImpl<T>::getEndianness() const {
+    return mEnd;
+}
+
+template<typename T>
+uint32_t TiffEntryImpl<T>::getComparableValue() const {
+    return mTag;
+}
+
+template<typename T>
+status_t TiffEntryImpl<T>::writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const {
+    assert((offset % TIFF_WORD_SIZE) == 0);
+    status_t ret = OK;
+    BAIL_ON_FAIL(out->write(&mTag, 0, 1), ret);
+    BAIL_ON_FAIL(out->write(&mType, 0, 1), ret);
+    BAIL_ON_FAIL(out->write(&mCount, 0, 1), ret);
+
+    uint32_t dataSize = getActualSize();
+    if (dataSize > OFFSET_SIZE) {
+        BAIL_ON_FAIL(out->write(&offset, 0, 1), ret);
+    } else {
+        uint32_t count = mCount;
+        if (getType() == RATIONAL || getType() == SRATIONAL) {
+            /**
+             * Rationals are stored as an array of ints.  Each
+             * rational is represented by 2 ints.  To recover the
+             * size of the array here, multiply the count by 2.
+             */
+            count <<= 1;
+        }
+        BAIL_ON_FAIL(out->write(mData, 0, count), ret);
+        ZERO_TILL_WORD(out, dataSize, ret);
+    }
+    return ret;
+}
+
+template<typename T>
+status_t TiffEntryImpl<T>::writeData(uint32_t offset, EndianOutput* out) const {
+    status_t ret = OK;
+
+    // Some tags have fixed-endian value output
+    Endianness tmp = UNDEFINED_ENDIAN;
+    if (mEnd != UNDEFINED_ENDIAN) {
+        tmp = out->getEndianness();
+        out->setEndianness(mEnd);
+    }
+
+    uint32_t count = mCount;
+    if (getType() == RATIONAL || getType() == SRATIONAL) {
+        /**
+         * Rationals are stored as an array of ints.  Each
+         * rational is represented by 2 ints.  To recover the
+         * size of the array here, multiply the count by 2.
+         */
+        count <<= 1;
+    }
+
+    BAIL_ON_FAIL(out->write(mData, 0, count), ret);
+
+    if (mEnd != UNDEFINED_ENDIAN) {
+        out->setEndianness(tmp);
+    }
+
+    // Write to next word alignment
+    ZERO_TILL_WORD(out, sizeof(T) * count, ret);
+    return ret;
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_TIFF_ENTRY_IMPL*/
+
+
diff --git a/media/img_utils/include/img_utils/TiffHelpers.h b/media/img_utils/include/img_utils/TiffHelpers.h
new file mode 100644
index 0000000..fd0ea7a
--- /dev/null
+++ b/media/img_utils/include/img_utils/TiffHelpers.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_TIFF_HELPERS_H
+#define IMG_UTILS_TIFF_HELPERS_H
+
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+const uint8_t ZERO_WORD[] = {0, 0, 0, 0};
+
+#define BAIL_ON_FAIL(x, flag) \
+    if ((flag = (x)) != OK) return flag;
+
+#define BYTES_TILL_WORD(index) \
+    ((TIFF_WORD_SIZE - ((index) % TIFF_WORD_SIZE)) % TIFF_WORD_SIZE)
+
+#define WORD_ALIGN(count) \
+    count += BYTES_TILL_WORD(count);
+
+#define ZERO_TILL_WORD(output, index, ret) \
+    { \
+        size_t remaining = BYTES_TILL_WORD(index); \
+        if (remaining > 0) { \
+            BAIL_ON_FAIL(output->write(ZERO_WORD, 0, remaining), ret); \
+        } \
+    }
+
+/**
+ * Basic TIFF header constants.
+ */
+enum {
+    BAD_OFFSET = 0,
+    TIFF_WORD_SIZE = 4, // Size in bytes
+    IFD_HEADER_SIZE = 2, // Size in bytes
+    IFD_FOOTER_SIZE = 4, // Size in bytes
+    TIFF_ENTRY_SIZE = 12, // Size in bytes
+    MAX_IFD_ENTRIES = UINT16_MAX,
+    FILE_HEADER_SIZE = 8, // Size in bytes
+    ENDIAN_MARKER_SIZE = 2, // Size in bytes
+    TIFF_MARKER_SIZE = 2, // Size in bytes
+    OFFSET_MARKER_SIZE = 4, // Size in bytes
+    TIFF_FILE_MARKER = 42,
+    BIG_ENDIAN_MARKER = 0x4D4Du,
+    LITTLE_ENDIAN_MARKER = 0x4949u
+};
+
+/**
+ * Constants for the TIFF tag types.
+ */
+enum TagType {
+    UNKNOWN_TAGTYPE = 0,
+    BYTE=1,
+    ASCII,
+    SHORT,
+    LONG,
+    RATIONAL,
+    SBYTE,
+    UNDEFINED,
+    SSHORT,
+    SLONG,
+    SRATIONAL,
+    FLOAT,
+    DOUBLE
+};
+
+/**
+ * Sizes of the TIFF entry fields (in bytes).
+ */
+enum {
+    TAG_SIZE = 2,
+    TYPE_SIZE = 2,
+    COUNT_SIZE = 4,
+    OFFSET_SIZE = 4
+};
+
+/**
+ * Convenience IFD id constants.
+ */
+enum {
+    IFD_0 = 0,
+    RAW_IFD,
+    PROFILE_IFD,
+    PREVIEW_IFD
+};
+
+inline size_t getTypeSize(TagType type) {
+    switch(type) {
+        case UNDEFINED:
+        case ASCII:
+        case BYTE:
+        case SBYTE:
+            return 1;
+        case SHORT:
+        case SSHORT:
+            return 2;
+        case LONG:
+        case SLONG:
+        case FLOAT:
+            return 4;
+        case RATIONAL:
+        case SRATIONAL:
+        case DOUBLE:
+            return 8;
+        default:
+            return 0;
+    }
+}
+
+inline uint32_t calculateIfdSize(size_t numberOfEntries) {
+    return IFD_HEADER_SIZE + IFD_FOOTER_SIZE + TIFF_ENTRY_SIZE * numberOfEntries;
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_TIFF_HELPERS_H*/
diff --git a/media/img_utils/include/img_utils/TiffIfd.h b/media/img_utils/include/img_utils/TiffIfd.h
new file mode 100644
index 0000000..9400456
--- /dev/null
+++ b/media/img_utils/include/img_utils/TiffIfd.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_TIFF_IFD_H
+#define IMG_UTILS_TIFF_IFD_H
+
+#include <img_utils/TiffWritable.h>
+#include <img_utils/TiffEntry.h>
+#include <img_utils/Output.h>
+#include <img_utils/SortedEntryVector.h>
+
+#include <cutils/compiler.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/SortedVector.h>
+#include <utils/StrongPointer.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * This class holds a single TIFF Image File Directory (IFD) structure.
+ *
+ * This maps to the TIFF IFD structure that is logically composed of:
+ * - A 2-byte field listing the number of entries.
+ * - A list of 12-byte TIFF entries.
+ * - A 4-byte offset to the next IFD.
+ */
+class ANDROID_API TiffIfd : public TiffWritable {
+    public:
+        // TODO: Copy constructor/equals here - needed for SubIfds.
+        TiffIfd(uint32_t ifdId);
+        virtual ~TiffIfd();
+
+        /**
+         * Add a TiffEntry to this IFD or replace an existing entry with the
+         * same tag ID.  No validation is done.
+         *
+         * Returns OK on success, or a negative error code on failure.
+         */
+        virtual status_t addEntry(const sp<TiffEntry>& entry);
+
+        /**
+         * Set the pointer to the next IFD.  This is used to create a linked
+         * list of IFDs as defined by the TIFF 6.0 spec., and is not included
+         * when calculating the size of IFD and entries for the getSize()
+         * method (unlike SubIFDs).
+         */
+        virtual void setNextIfd(const sp<TiffIfd>& ifd);
+
+        /**
+         * Get the pointer to the next IFD, or NULL if none exists.
+         */
+        virtual sp<TiffIfd> getNextIfd() const;
+
+        /**
+         * Write the IFD data.  This includes the IFD header, entries, footer,
+         * and the corresponding values for each entry (recursively including
+         * sub-IFDs).  The written amount should end on a word boundary, and
+         * the given offset should be word aligned.
+         *
+         * Returns OK on success, or a negative error code on failure.
+         */
+        virtual status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const;
+
+        /**
+         * Get the size of the IFD. This includes the IFD header, entries, footer,
+         * and the corresponding values for each entry (recursively including
+         * any sub-IFDs).
+         */
+        virtual size_t getSize() const;
+
+        /**
+         * Get the id of this IFD.
+         */
+        virtual uint32_t getId() const;
+
+        /**
+         * Get an entry with the given tag ID.
+         *
+         * Returns a strong pointer to the entry if it exists, or an empty strong
+         * pointer.
+         */
+        virtual sp<TiffEntry> getEntry(uint16_t tag) const;
+
+        /**
+         * Get a formatted string representing this IFD.
+         */
+        String8 toString() const;
+
+        /**
+         * Print a formatted string representing this IFD to logcat.
+         */
+        void log() const;
+
+        /**
+         * Get value used to determine sort order.
+         */
+        virtual uint32_t getComparableValue() const;
+    protected:
+        virtual uint32_t checkAndGetOffset(uint32_t offset) const;
+        SortedEntryVector mEntries;
+        sp<TiffIfd> mNextIfd;
+        uint32_t mIfdId;
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_TIFF_IFD_H*/
diff --git a/media/img_utils/include/img_utils/TiffWritable.h b/media/img_utils/include/img_utils/TiffWritable.h
new file mode 100644
index 0000000..a72cecc
--- /dev/null
+++ b/media/img_utils/include/img_utils/TiffWritable.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_TIFF_WRITABLE
+#define IMG_UTILS_TIFF_WRITABLE
+
+#include <img_utils/Orderable.h>
+#include <img_utils/EndianUtils.h>
+#include <img_utils/Output.h>
+
+#include <cutils/compiler.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+/**
+ * TiffWritable subclasses represent TIFF metadata objects that can be written
+ * to an EndianOutput object.  This is used for TIFF entries and IFDs.
+ */
+class ANDROID_API TiffWritable : public Orderable, public LightRefBase<TiffWritable> {
+    public:
+        TiffWritable();
+        virtual ~TiffWritable();
+
+        /**
+         * Write the data to the output. The given offset is used to calculate
+         * the header offset for values written.  The offset is defined
+         * relative to the beginning of the TIFF header, and is word aligned.
+         *
+         * Returns OK on success, or a negative error code on failure.
+         */
+        virtual status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const = 0;
+
+        /**
+         * Get the size of the data to write.
+         */
+        virtual size_t getSize() const = 0;
+
+};
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+#endif /*IMG_UTILS_TIFF_WRITABLE*/
diff --git a/media/img_utils/include/img_utils/TiffWriter.h b/media/img_utils/include/img_utils/TiffWriter.h
new file mode 100644
index 0000000..ec27fc3
--- /dev/null
+++ b/media/img_utils/include/img_utils/TiffWriter.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2014 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 IMG_UTILS_TIFF_WRITER_H
+#define IMG_UTILS_TIFF_WRITER_H
+
+#include <img_utils/EndianUtils.h>
+#include <img_utils/TiffEntryImpl.h>
+#include <img_utils/TagDefinitions.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
+
+#include <cutils/compiler.h>
+#include <stdint.h>
+
+namespace android {
+namespace img_utils {
+
+class TiffEntry;
+class TiffIfd;
+class Output;
+
+/**
+ * This class holds a collection of TIFF IFDs that can be written as a
+ * complete DNG file header.
+ *
+ * This maps to the TIFF header structure that is logically composed of:
+ * - An 8-byte file header containing an endianness indicator, the TIFF
+ *   file marker, and the offset to the first IFD.
+ * - A list of TIFF IFD structures.
+ */
+class ANDROID_API TiffWriter : public LightRefBase<TiffWriter> {
+    public:
+
+        /**
+         * Constructs a TiffWriter with the default tag mappings. This enables
+         * all of the tags defined in TagDefinitions.h, and uses the following
+         * mapping precedence to resolve collisions:
+         * (highest precedence) TIFF/EP > DNG > EXIF 2.3 > TIFF 6.0
+         */
+        TiffWriter();
+
+        /**
+         * Constructs a TiffWriter with the given tag mappings.  The mapping
+         * precedence will be in the order that the definition maps are given,
+         * where the lower index map gets precedence.
+         *
+         * This can be used with user-defined definitions, or definitions form
+         * TagDefinitions.h
+         *
+         * The enabledDefinitions mapping object is owned by the caller, and must
+         * stay alive for the lifespan of the constructed TiffWriter object.
+         */
+        TiffWriter(KeyedVector<uint16_t, const TagDefinition_t*>* enabledDefinitions,
+                size_t length);
+
+        virtual ~TiffWriter();
+
+        /**
+         * Write a TIFF header containing each IFD set.  This will recursively
+         * write all SubIFDs and tags.
+         *
+         * Returns OK on success, or a negative error code on failure.
+         */
+        virtual status_t write(Output* out, Endianness end = LITTLE);
+
+        /**
+         * Get the total size in bytes of the TIFF header.  This includes all
+         * IFDs, tags, and values set for this TiffWriter.
+         */
+        virtual uint32_t getTotalSize() const;
+
+        /**
+         * Add the given entry to its default IFD.  If that IFD does not
+         * exist, it will be created.
+         */
+        virtual status_t addEntry(const sp<TiffEntry>& entry);
+
+        /**
+         * Build an entry for a known tag.  This tag must be one of the tags
+         * defined in one of the definition vectors this TIFF writer was constructed
+         * with. The count and type are validated. If this succeeds, the resulting
+         * entry will be placed in the outEntry pointer.
+         *
+         * Returns OK on success, or a negative error code on failure. Valid
+         * error codes for this method are:
+         * - BAD_INDEX - The given tag doesn't exist.
+         * - BAD_VALUE - The given count doesn't match the required count for
+         *               this tag.
+         * - BAD_TYPE  - The type of the given data isn't compatible with the
+         *               type required for this tag.
+         */
+        template<typename T>
+        status_t buildEntry(uint16_t tag, uint32_t count, const T* data,
+                  /*out*/sp<TiffEntry>* outEntry) const;
+
+         /**
+         * Build an entry for a known tag and add it to the IFD with the given ID.
+         * This tag must be defined in one of the definition vectors this TIFF writer
+         * was constructed with. The count and type are validated. If this succeeds,
+         * the resulting entry will be placed in the outEntry pointer.
+         *
+         * Returns OK on success, or a negative error code on failure. Valid
+         * error codes for this method are:
+         * - BAD_INDEX - The given tag doesn't exist.
+         * - BAD_VALUE - The given count doesn't match the required count for
+         *               this tag.
+         * - BAD_TYPE  - The type of the given data isn't compatible with the
+         *               type required for this tag.
+         * - NAME_NOT_FOUND - No ifd exists with the given ID.
+         */
+        template<typename T>
+        status_t addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd);
+
+        /**
+         * Return the TIFF entry with the given tag ID in the IFD with the given ID,
+         * or an empty pointer if none exists.
+         */
+        virtual sp<TiffEntry> getEntry(uint16_t tag, uint32_t ifd) const;
+
+        /**
+         * Add the given IFD to the end of the top-level IFD chain. No
+         * validation is done.
+         *
+         * Returns OK on success, or a negative error code on failure.
+         */
+        virtual status_t uncheckedAddIfd(const sp<TiffIfd>& ifd);
+
+        /**
+         * Create an empty IFD with the given ID and add it to the end of the
+         * list of IFDs.
+         */
+        virtual status_t addIfd(uint32_t ifd);
+
+        /**
+         * Build an entry.  No validation is done.
+         *
+         * WARNING: Using this method can result in creating poorly formatted
+         * TIFF files.
+         *
+         * Returns a TiffEntry with the given tag, type, count, endianness,
+         * and data.
+         */
+        template<typename T>
+        static sp<TiffEntry> uncheckedBuildEntry(uint16_t tag, TagType type,
+                  uint32_t count, Endianness end, const T* data);
+
+        /**
+         * Utility function to build atag-to-definition mapping from a given
+         * array of tag definitions.
+         */
+        static KeyedVector<uint16_t, const TagDefinition_t*> buildTagMap(
+                  const TagDefinition_t* definitions, size_t length);
+
+        /**
+         * Returns the default type for the given tag ID.
+         */
+        virtual TagType getDefaultType(uint16_t tag) const;
+
+        /**
+         * Returns the default count for a given tag ID, or 0 if this
+         * tag normally has a variable count.
+         */
+        virtual uint32_t getDefaultCount(uint16_t tag) const;
+
+        /**
+         * Returns true if a definition exist for the given tag ID.
+         */
+        virtual bool checkIfDefined(uint16_t tag) const;
+
+        /**
+         * Print the currently configured IFDs and entries to logcat.
+         */
+        virtual void log() const;
+
+    protected:
+        enum {
+            DEFAULT_NUM_TAG_MAPS = 4,
+        };
+
+        sp<TiffIfd> findLastIfd();
+        status_t writeFileHeader(EndianOutput& out);
+        const TagDefinition_t* lookupDefinition(uint16_t tag) const;
+        status_t calculateOffsets();
+
+        sp<TiffIfd> mIfd;
+        KeyedVector<uint32_t, sp<TiffIfd> > mNamedIfds;
+        KeyedVector<uint16_t, const TagDefinition_t*>* mTagMaps;
+        size_t mNumTagMaps;
+
+        static KeyedVector<uint16_t, const TagDefinition_t*> sTagMaps[];
+};
+
+template<typename T>
+status_t TiffWriter::buildEntry(uint16_t tag, uint32_t count, const T* data,
+                  /*out*/sp<TiffEntry>* outEntry) const {
+    const TagDefinition_t* definition = lookupDefinition(tag);
+
+    if (definition == NULL) {
+        ALOGE("%s: No such tag exists for id %x.", __FUNCTION__, tag);
+        return BAD_INDEX;
+    }
+
+    uint32_t fixedCount = definition->fixedCount;
+    if (fixedCount > 0 && fixedCount != count) {
+        ALOGE("%s: Invalid count %d for tag %x (expects %d).", __FUNCTION__, count, tag,
+                fixedCount);
+        return BAD_VALUE;
+    }
+
+    TagType fixedType = definition->defaultType;
+    if (TiffEntry::forceValidType(fixedType, data) == NULL) {
+        ALOGE("%s: Invalid type used for tag value for tag %x.", __FUNCTION__, tag);
+        return BAD_TYPE;
+    }
+
+    *outEntry = new TiffEntryImpl<T>(tag, fixedType, count,
+        definition->fixedEndian, data);
+
+    return OK;
+}
+
+template<typename T>
+status_t TiffWriter::addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd) {
+    sp<TiffEntry> outEntry;
+    status_t ret = buildEntry<T>(tag, count, data, &outEntry);
+    if (ret != OK) {
+        ALOGE("%s: Could not build entry for tag %x.", __FUNCTION__, tag);
+        return ret;
+    }
+    ssize_t index = mNamedIfds.indexOfKey(ifd);
+    if (index < 0) {
+        ALOGE("%s: No IFD %d set for this writer.", __FUNCTION__, ifd);
+        return NAME_NOT_FOUND;
+    }
+    return mNamedIfds[index]->addEntry(outEntry);
+}
+
+template<typename T>
+sp<TiffEntry> TiffWriter::uncheckedBuildEntry(uint16_t tag, TagType type, uint32_t count,
+        Endianness end, const T* data) {
+    TiffEntryImpl<T>* entry = new TiffEntryImpl<T>(tag, type, count, end, data);
+    return sp<TiffEntry>(entry);
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
+
+#endif /*IMG_UTILS_TIFF_WRITER_H*/
diff --git a/media/img_utils/src/Android.mk b/media/img_utils/src/Android.mk
new file mode 100644
index 0000000..80893be
--- /dev/null
+++ b/media/img_utils/src/Android.mk
@@ -0,0 +1,61 @@
+# Copyright 2014 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+  EndianUtils.cpp \
+  FileInput.cpp \
+  FileOutput.cpp \
+  SortedEntryVector.cpp \
+  Input.cpp \
+  Output.cpp \
+  Orderable.cpp \
+  TiffIfd.cpp \
+  TiffWritable.cpp \
+  TiffWriter.cpp \
+  TiffEntry.cpp \
+  TiffEntryImpl.cpp \
+  ByteArrayOutput.cpp \
+  DngUtils.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+  libexpat \
+  libutils \
+  libcutils \
+  libcamera_metadata \
+  libcamera_client
+
+LOCAL_C_INCLUDES += \
+  $(LOCAL_PATH)/../include \
+  system/media/camera/include
+
+LOCAL_CFLAGS += \
+  -Wall \
+  -Wextra \
+  -Werror \
+  -fvisibility=hidden
+
+ifneq ($(filter userdebug eng,$(TARGET_BUILD_VARIANT)),)
+    # Enable assert() in eng builds
+    LOCAL_CFLAGS += -UNDEBUG -DLOG_NDEBUG=1
+endif
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/../include
+
+LOCAL_MODULE := libimg_utils
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/img_utils/src/ByteArrayOutput.cpp b/media/img_utils/src/ByteArrayOutput.cpp
new file mode 100644
index 0000000..db2d248
--- /dev/null
+++ b/media/img_utils/src/ByteArrayOutput.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 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 <img_utils/ByteArrayOutput.h>
+
+#include <utils/Log.h>
+
+namespace android {
+namespace img_utils {
+
+ByteArrayOutput::ByteArrayOutput() {}
+
+ByteArrayOutput::~ByteArrayOutput() {}
+
+status_t ByteArrayOutput::open() {
+    return OK;
+}
+
+status_t ByteArrayOutput::write(const uint8_t* buf, size_t offset, size_t count) {
+    if (mByteArray.appendArray(buf + offset, count) < 0) {
+        ALOGE("%s: Failed to write to ByteArrayOutput.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    return OK;
+}
+
+status_t ByteArrayOutput::close() {
+    mByteArray.clear();
+    return OK;
+}
+
+size_t ByteArrayOutput::getSize() const {
+    return mByteArray.size();
+}
+
+const uint8_t* ByteArrayOutput::getArray() const {
+    return mByteArray.array();
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/DngUtils.cpp b/media/img_utils/src/DngUtils.cpp
new file mode 100644
index 0000000..788dfc8
--- /dev/null
+++ b/media/img_utils/src/DngUtils.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2014 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 <img_utils/DngUtils.h>
+
+namespace android {
+namespace img_utils {
+
+OpcodeListBuilder::OpcodeListBuilder() : mOpList(), mEndianOut(&mOpList, BIG) {
+    if(mEndianOut.open() != OK) {
+        ALOGE("%s: Open failed.", __FUNCTION__);
+    }
+}
+
+OpcodeListBuilder::~OpcodeListBuilder() {
+    if(mEndianOut.close() != OK) {
+        ALOGE("%s: Close failed.", __FUNCTION__);
+    }
+}
+
+size_t OpcodeListBuilder::getSize() const {
+    return mOpList.getSize() + sizeof(mCount);
+}
+
+uint32_t OpcodeListBuilder::getCount() const {
+    return mCount;
+}
+
+status_t OpcodeListBuilder::buildOpList(uint8_t* buf) const {
+    uint32_t count = convertToBigEndian(mCount);
+    memcpy(buf, &count, sizeof(count));
+    memcpy(buf + sizeof(count), mOpList.getArray(), mOpList.getSize());
+    return OK;
+}
+
+status_t OpcodeListBuilder::addGainMapsForMetadata(uint32_t lsmWidth,
+                                                   uint32_t lsmHeight,
+                                                   uint32_t activeAreaTop,
+                                                   uint32_t activeAreaLeft,
+                                                   uint32_t activeAreaBottom,
+                                                   uint32_t activeAreaRight,
+                                                   CfaLayout cfa,
+                                                   const float* lensShadingMap) {
+    uint32_t activeAreaWidth = activeAreaRight - activeAreaLeft;
+    uint32_t activeAreaHeight = activeAreaBottom - activeAreaTop;
+    double spacingV = 1.0 / lsmHeight;
+    double spacingH = 1.0 / lsmWidth;
+
+    float redMap[lsmWidth * lsmHeight];
+    float greenEvenMap[lsmWidth * lsmHeight];
+    float greenOddMap[lsmWidth * lsmHeight];
+    float blueMap[lsmWidth * lsmHeight];
+
+    size_t lsmMapSize = lsmWidth * lsmHeight * 4;
+
+    // Split lens shading map channels into separate arrays
+    size_t j = 0;
+    for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
+        redMap[j] = lensShadingMap[i + LSM_R_IND];
+        greenEvenMap[j] = lensShadingMap[i + LSM_GE_IND];
+        greenOddMap[j] = lensShadingMap[i + LSM_GO_IND];
+        blueMap[j] = lensShadingMap[i + LSM_B_IND];
+    }
+
+    uint32_t redTop = 0;
+    uint32_t redLeft = 0;
+    uint32_t greenEvenTop = 0;
+    uint32_t greenEvenLeft = 1;
+    uint32_t greenOddTop = 1;
+    uint32_t greenOddLeft = 0;
+    uint32_t blueTop = 1;
+    uint32_t blueLeft = 1;
+
+    switch(cfa) {
+        case CFA_RGGB:
+            redTop = 0;
+            redLeft = 0;
+            greenEvenTop = 0;
+            greenEvenLeft = 1;
+            greenOddTop = 1;
+            greenOddLeft = 0;
+            blueTop = 1;
+            blueLeft = 1;
+            break;
+        case CFA_GRBG:
+            redTop = 0;
+            redLeft = 1;
+            greenEvenTop = 0;
+            greenEvenLeft = 0;
+            greenOddTop = 1;
+            greenOddLeft = 1;
+            blueTop = 1;
+            blueLeft = 0;
+            break;
+        case CFA_GBRG:
+            redTop = 1;
+            redLeft = 0;
+            greenEvenTop = 0;
+            greenEvenLeft = 0;
+            greenOddTop = 1;
+            greenOddLeft = 1;
+            blueTop = 0;
+            blueLeft = 1;
+            break;
+        case CFA_BGGR:
+            redTop = 1;
+            redLeft = 1;
+            greenEvenTop = 0;
+            greenEvenLeft = 1;
+            greenOddTop = 1;
+            greenOddLeft = 0;
+            blueTop = 0;
+            blueLeft = 0;
+            break;
+        default:
+            ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
+            return BAD_VALUE;
+    }
+
+    status_t err = addGainMap(/*top*/redTop,
+                              /*left*/redLeft,
+                              /*bottom*/activeAreaHeight - 1,
+                              /*right*/activeAreaWidth - 1,
+                              /*plane*/0,
+                              /*planes*/1,
+                              /*rowPitch*/2,
+                              /*colPitch*/2,
+                              /*mapPointsV*/lsmHeight,
+                              /*mapPointsH*/lsmWidth,
+                              /*mapSpacingV*/spacingV,
+                              /*mapSpacingH*/spacingH,
+                              /*mapOriginV*/0,
+                              /*mapOriginH*/0,
+                              /*mapPlanes*/1,
+                              /*mapGains*/redMap);
+    if (err != OK) return err;
+
+    err = addGainMap(/*top*/greenEvenTop,
+                     /*left*/greenEvenLeft,
+                     /*bottom*/activeAreaHeight - 1,
+                     /*right*/activeAreaWidth - 1,
+                     /*plane*/0,
+                     /*planes*/1,
+                     /*rowPitch*/2,
+                     /*colPitch*/2,
+                     /*mapPointsV*/lsmHeight,
+                     /*mapPointsH*/lsmWidth,
+                     /*mapSpacingV*/spacingV,
+                     /*mapSpacingH*/spacingH,
+                     /*mapOriginV*/0,
+                     /*mapOriginH*/0,
+                     /*mapPlanes*/1,
+                     /*mapGains*/greenEvenMap);
+    if (err != OK) return err;
+
+    err = addGainMap(/*top*/greenOddTop,
+                     /*left*/greenOddLeft,
+                     /*bottom*/activeAreaHeight - 1,
+                     /*right*/activeAreaWidth - 1,
+                     /*plane*/0,
+                     /*planes*/1,
+                     /*rowPitch*/2,
+                     /*colPitch*/2,
+                     /*mapPointsV*/lsmHeight,
+                     /*mapPointsH*/lsmWidth,
+                     /*mapSpacingV*/spacingV,
+                     /*mapSpacingH*/spacingH,
+                     /*mapOriginV*/0,
+                     /*mapOriginH*/0,
+                     /*mapPlanes*/1,
+                     /*mapGains*/greenOddMap);
+    if (err != OK) return err;
+
+    err = addGainMap(/*top*/blueTop,
+                     /*left*/blueLeft,
+                     /*bottom*/activeAreaHeight - 1,
+                     /*right*/activeAreaWidth - 1,
+                     /*plane*/0,
+                     /*planes*/1,
+                     /*rowPitch*/2,
+                     /*colPitch*/2,
+                     /*mapPointsV*/lsmHeight,
+                     /*mapPointsH*/lsmWidth,
+                     /*mapSpacingV*/spacingV,
+                     /*mapSpacingH*/spacingH,
+                     /*mapOriginV*/0,
+                     /*mapOriginH*/0,
+                     /*mapPlanes*/1,
+                     /*mapGains*/blueMap);
+    return err;
+}
+
+status_t OpcodeListBuilder::addGainMap(uint32_t top,
+                                       uint32_t left,
+                                       uint32_t bottom,
+                                       uint32_t right,
+                                       uint32_t plane,
+                                       uint32_t planes,
+                                       uint32_t rowPitch,
+                                       uint32_t colPitch,
+                                       uint32_t mapPointsV,
+                                       uint32_t mapPointsH,
+                                       double mapSpacingV,
+                                       double mapSpacingH,
+                                       double mapOriginV,
+                                       double mapOriginH,
+                                       uint32_t mapPlanes,
+                                       const float* mapGains) {
+
+    uint32_t opcodeId = GAIN_MAP_ID;
+
+    status_t err = mEndianOut.write(&opcodeId, 0, 1);
+    if (err != OK) return err;
+
+    uint8_t version[] = {1, 3, 0, 0};
+    err = mEndianOut.write(version, 0, NELEMS(version));
+    if (err != OK) return err;
+
+    uint32_t flags = FLAG_OPTIONAL | FLAG_OPTIONAL_FOR_PREVIEW;
+    err = mEndianOut.write(&flags, 0, 1);
+    if (err != OK) return err;
+
+    const uint32_t NUMBER_INT_ARGS = 11;
+    const uint32_t NUMBER_DOUBLE_ARGS = 4;
+
+    uint32_t totalSize = NUMBER_INT_ARGS * sizeof(uint32_t) + NUMBER_DOUBLE_ARGS * sizeof(double) +
+            mapPointsV * mapPointsH * mapPlanes * sizeof(float);
+
+    err = mEndianOut.write(&totalSize, 0, 1);
+    if (err != OK) return err;
+
+    // Batch writes as much as possible
+    uint32_t settings1[] = { top,
+                            left,
+                            bottom,
+                            right,
+                            plane,
+                            planes,
+                            rowPitch,
+                            colPitch,
+                            mapPointsV,
+                            mapPointsH };
+
+    err = mEndianOut.write(settings1, 0, NELEMS(settings1));
+    if (err != OK) return err;
+
+    double settings2[] = { mapSpacingV,
+                          mapSpacingH,
+                          mapOriginV,
+                          mapOriginH };
+
+    err = mEndianOut.write(settings2, 0, NELEMS(settings2));
+    if (err != OK) return err;
+
+    err = mEndianOut.write(&mapPlanes, 0, 1);
+    if (err != OK) return err;
+
+    err = mEndianOut.write(mapGains, 0, mapPointsV * mapPointsH * mapPlanes);
+    if (err != OK) return err;
+
+    mCount++;
+
+    return OK;
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/EndianUtils.cpp b/media/img_utils/src/EndianUtils.cpp
new file mode 100644
index 0000000..8681cbe
--- /dev/null
+++ b/media/img_utils/src/EndianUtils.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 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 <img_utils/EndianUtils.h>
+
+namespace android {
+namespace img_utils {
+
+EndianOutput::EndianOutput(Output* out, Endianness end)
+        : mOffset(0), mOutput(out), mEndian(end) {}
+
+EndianOutput::~EndianOutput() {}
+
+status_t EndianOutput::open() {
+    mOffset = 0;
+    return mOutput->open();
+}
+
+status_t EndianOutput::close() {
+    return mOutput->close();
+}
+
+void EndianOutput::setEndianness(Endianness end) {
+    mEndian = end;
+}
+
+uint32_t EndianOutput::getCurrentOffset() const {
+    return mOffset;
+}
+
+Endianness EndianOutput::getEndianness() const {
+    return mEndian;
+}
+
+status_t EndianOutput::write(const uint8_t* buf, size_t offset, size_t count) {
+    status_t res = OK;
+    if((res = mOutput->write(buf, offset, count)) == OK) {
+        mOffset += count;
+    }
+    return res;
+}
+
+status_t EndianOutput::write(const int8_t* buf, size_t offset, size_t count) {
+    return write(reinterpret_cast<const uint8_t*>(buf), offset, count);
+}
+
+#define DEFINE_WRITE(_type_) \
+status_t EndianOutput::write(const _type_* buf, size_t offset, size_t count) { \
+    return writeHelper<_type_>(buf, offset, count); \
+}
+
+DEFINE_WRITE(uint16_t)
+DEFINE_WRITE(int16_t)
+DEFINE_WRITE(uint32_t)
+DEFINE_WRITE(int32_t)
+DEFINE_WRITE(uint64_t)
+DEFINE_WRITE(int64_t)
+
+status_t EndianOutput::write(const float* buf, size_t offset, size_t count) {
+    assert(sizeof(float) == sizeof(uint32_t));
+    return writeHelper<uint32_t>(reinterpret_cast<const uint32_t*>(buf), offset, count);
+}
+
+status_t EndianOutput::write(const double* buf, size_t offset, size_t count) {
+    assert(sizeof(double) == sizeof(uint64_t));
+    return writeHelper<uint64_t>(reinterpret_cast<const uint64_t*>(buf), offset, count);
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/FileInput.cpp b/media/img_utils/src/FileInput.cpp
new file mode 100644
index 0000000..e43fd53
--- /dev/null
+++ b/media/img_utils/src/FileInput.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014 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 <img_utils/FileInput.h>
+
+#include <utils/Log.h>
+
+namespace android {
+namespace img_utils {
+
+FileInput::FileInput(String8 path) : mFp(NULL), mPath(path), mOpen(false) {}
+
+FileInput::~FileInput() {
+    if (mOpen) {
+        ALOGE("%s: FileInput destroyed without calling close!", __FUNCTION__);
+        close();
+    }
+
+}
+
+status_t FileInput::open() {
+    if (mOpen) {
+        ALOGW("%s: Open called when file %s already open.", __FUNCTION__, mPath.string());
+        return OK;
+    }
+    mFp = ::fopen(mPath, "rb");
+    if (!mFp) {
+        ALOGE("%s: Could not open file %s", __FUNCTION__, mPath.string());
+        return BAD_VALUE;
+    }
+    mOpen = true;
+    return OK;
+}
+
+size_t FileInput::read(uint8_t* buf, size_t offset, size_t count, status_t* err) {
+    if (!mOpen) {
+        ALOGE("%s: Could not read file %s, file not open.", __FUNCTION__, mPath.string());
+        if (err != NULL) *err = BAD_VALUE;
+        return 0;
+    }
+
+    size_t bytesRead = ::fread(buf + offset, sizeof(uint8_t), count, mFp);
+    int error = ::ferror(mFp);
+    if (error != 0) {
+        ALOGE("%s: Error %d occurred while reading file %s.", __FUNCTION__, error, mPath.string());
+        if (err != NULL) *err = BAD_VALUE;
+    }
+    return bytesRead;
+}
+
+status_t FileInput::close() {
+    if(!mOpen) {
+        ALOGW("%s: Close called when file %s already close.", __FUNCTION__, mPath.string());
+        return OK;
+    }
+
+    status_t ret = OK;
+    if(::fclose(mFp) != 0) {
+        ALOGE("%s: Failed to close file %s.", __FUNCTION__, mPath.string());
+        ret = BAD_VALUE;
+    }
+    mOpen = false;
+    return OK;
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/FileOutput.cpp b/media/img_utils/src/FileOutput.cpp
new file mode 100644
index 0000000..ce763ff
--- /dev/null
+++ b/media/img_utils/src/FileOutput.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 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 <img_utils/FileOutput.h>
+
+#include <utils/Log.h>
+
+namespace android {
+namespace img_utils {
+
+FileOutput::FileOutput(String8 path) : mFp(NULL), mPath(path), mOpen(false) {}
+
+FileOutput::~FileOutput() {
+    if (mOpen) {
+        ALOGW("%s: Destructor called with %s still open.", __FUNCTION__, mPath.string());
+        close();
+    }
+}
+
+status_t FileOutput::open() {
+    if (mOpen) {
+        ALOGW("%s: Open called when file %s already open.", __FUNCTION__, mPath.string());
+        return OK;
+    }
+    mFp = ::fopen(mPath, "wb");
+    if (!mFp) {
+        ALOGE("%s: Could not open file %s", __FUNCTION__, mPath.string());
+        return BAD_VALUE;
+    }
+    mOpen = true;
+    return OK;
+}
+
+status_t FileOutput::write(const uint8_t* buf, size_t offset, size_t count) {
+    if (!mOpen) {
+        ALOGE("%s: Could not write file %s, file not open.", __FUNCTION__, mPath.string());
+        return BAD_VALUE;
+    }
+
+    ::fwrite(buf + offset, sizeof(uint8_t), count, mFp);
+
+    int error = ::ferror(mFp);
+    if (error != 0) {
+        ALOGE("%s: Error %d occurred while writing file %s.", __FUNCTION__, error, mPath.string());
+        return BAD_VALUE;
+    }
+    return OK;
+}
+
+status_t FileOutput::close() {
+    if(!mOpen) {
+        ALOGW("%s: Close called when file %s already close.", __FUNCTION__, mPath.string());
+        return OK;
+    }
+
+    status_t ret = OK;
+    if(::fclose(mFp) != 0) {
+        ALOGE("%s: Failed to close file %s.", __FUNCTION__, mPath.string());
+        ret = BAD_VALUE;
+    }
+    mOpen = false;
+    return OK;
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/Input.cpp b/media/img_utils/src/Input.cpp
new file mode 100644
index 0000000..1e51e10
--- /dev/null
+++ b/media/img_utils/src/Input.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 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 <img_utils/Input.h>
+
+namespace android {
+namespace img_utils {
+
+Input::~Input() {}
+status_t Input::open() { return OK; }
+status_t Input::close() { return OK; }
+
+
+} /*namespace img_utils*/
+} /*namespace android*/
+
diff --git a/media/img_utils/src/Orderable.cpp b/media/img_utils/src/Orderable.cpp
new file mode 100644
index 0000000..300f122
--- /dev/null
+++ b/media/img_utils/src/Orderable.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 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 <img_utils/Orderable.h>
+
+#include <utils/Log.h>
+
+namespace android {
+namespace img_utils {
+
+#define COMPARE(op) \
+bool Orderable::operator op (const Orderable& orderable) const { \
+    return getComparableValue() op orderable.getComparableValue(); \
+}
+
+COMPARE(>)
+COMPARE(<)
+COMPARE(>=)
+COMPARE(<=)
+COMPARE(==)
+COMPARE(!=)
+
+Orderable::~Orderable() {}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/Output.cpp b/media/img_utils/src/Output.cpp
new file mode 100644
index 0000000..0e395b9
--- /dev/null
+++ b/media/img_utils/src/Output.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 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 <img_utils/Output.h>
+
+namespace android {
+namespace img_utils {
+
+Output::~Output() {}
+status_t Output::open() { return OK; }
+status_t Output::close() { return OK; }
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/SortedEntryVector.cpp b/media/img_utils/src/SortedEntryVector.cpp
new file mode 100644
index 0000000..f0e1fa1
--- /dev/null
+++ b/media/img_utils/src/SortedEntryVector.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 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 <img_utils/SortedEntryVector.h>
+
+#include <utils/TypeHelpers.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace img_utils {
+
+SortedEntryVector::~SortedEntryVector() {}
+
+ssize_t SortedEntryVector::indexOfTag(uint16_t tag) const {
+    // TODO: Use binary search here.
+    for (size_t i = 0; i < size(); ++i) {
+        if (itemAt(i)->getTag() == tag) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+int SortedEntryVector::do_compare(const void* lhs, const void* rhs) const {
+    const sp<TiffEntry>* lEntry = reinterpret_cast<const sp<TiffEntry>*>(lhs);
+    const sp<TiffEntry>* rEntry = reinterpret_cast<const sp<TiffEntry>*>(rhs);
+    return compare_type(**lEntry, **rEntry);
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/TiffEntry.cpp b/media/img_utils/src/TiffEntry.cpp
new file mode 100644
index 0000000..e028827
--- /dev/null
+++ b/media/img_utils/src/TiffEntry.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2014 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 <img_utils/TiffIfd.h>
+#include <img_utils/TiffHelpers.h>
+#include <img_utils/TiffEntry.h>
+
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+namespace android {
+namespace img_utils {
+
+TiffEntry::~TiffEntry() {}
+
+/**
+ * Specialize for each valid type, including sub-IFDs.
+ *
+ * Values with types other than the ones given here should not compile.
+ */
+template<>
+const Vector<sp<TiffIfd> >* TiffEntry::forceValidType<Vector<sp<TiffIfd> > >(TagType type,
+          const Vector<sp<TiffIfd> >* value) {
+    if (type == LONG) {
+        return value;
+    }
+    ALOGE("%s: Value of type 'ifd vector' is not valid for tag with TIFF type %d.",
+            __FUNCTION__, type);
+    return NULL;
+}
+
+template<>
+const sp<TiffIfd>* TiffEntry::forceValidType<sp<TiffIfd> >(TagType type, const sp<TiffIfd>* value) {
+    if (type == LONG) {
+        return value;
+    }
+    ALOGE("%s: Value of type 'ifd' is not valid for tag with TIFF type %d.",
+            __FUNCTION__, type);
+    return NULL;
+}
+
+template<>
+const uint8_t* TiffEntry::forceValidType<uint8_t>(TagType type, const uint8_t* value) {
+    if (type == BYTE || type == ASCII || type == UNDEFINED) {
+        return value;
+    }
+    ALOGE("%s: Value of type 'uint8_t' is not valid for tag with TIFF type %d.",
+            __FUNCTION__, type);
+    return NULL;
+}
+
+template<>
+const int8_t* TiffEntry::forceValidType<int8_t>(TagType type, const int8_t* value) {
+    if (type == SBYTE || type == ASCII || type == UNDEFINED) {
+        return value;
+    }
+    ALOGE("%s: Value of type 'int8_t' is not valid for tag with TIFF type %d.",
+            __FUNCTION__, type);
+    return NULL;
+}
+
+template<>
+const uint16_t* TiffEntry::forceValidType<uint16_t>(TagType type, const uint16_t* value) {
+    if (type == SHORT) {
+        return value;
+    }
+    ALOGE("%s: Value of type 'uint16_t' is not valid for tag with TIFF type %d.",
+            __FUNCTION__, type);
+    return NULL;
+}
+
+template<>
+const int16_t* TiffEntry::forceValidType<int16_t>(TagType type, const int16_t* value) {
+    if (type == SSHORT) {
+        return value;
+    }
+    ALOGE("%s: Value of type 'int16_t' is not valid for tag with TIFF type %d.",
+            __FUNCTION__, type);
+    return NULL;
+}
+
+template<>
+const uint32_t* TiffEntry::forceValidType<uint32_t>(TagType type, const uint32_t* value) {
+    if (type == LONG || type == RATIONAL) {
+        return value;
+    }
+    ALOGE("%s: Value of type 'uint32_t' is not valid for tag with TIFF type %d.",
+            __FUNCTION__, type);
+    return NULL;
+}
+
+template<>
+const int32_t* TiffEntry::forceValidType<int32_t>(TagType type, const int32_t* value) {
+    if (type == SLONG || type == SRATIONAL) {
+        return value;
+    }
+    ALOGE("%s: Value of type 'int32_t' is not valid for tag with TIFF type %d.",
+            __FUNCTION__, type);
+    return NULL;
+}
+
+template<>
+const double* TiffEntry::forceValidType<double>(TagType type, const double* value) {
+    if (type == DOUBLE) {
+        return value;
+    }
+    ALOGE("%s: Value of type 'double' is not valid for tag with TIFF type %d.",
+            __FUNCTION__, type);
+    return NULL;
+}
+
+template<>
+const float* TiffEntry::forceValidType<float>(TagType type, const float* value) {
+    if (type == FLOAT) {
+        return value;
+    }
+    ALOGE("%s: Value of type 'float' is not valid for tag with TIFF type %d.",
+            __FUNCTION__, type);
+    return NULL;
+}
+
+String8 TiffEntry::toString() const {
+    String8 output;
+    uint32_t count = getCount();
+    output.appendFormat("[id: %x, type: %d, count: %u, value: '", getTag(), getType(), count);
+
+    size_t cappedCount = count;
+    if (count > MAX_PRINT_STRING_LENGTH) {
+        cappedCount = MAX_PRINT_STRING_LENGTH;
+    }
+
+    TagType type = getType();
+    switch (type) {
+        case UNDEFINED:
+        case BYTE: {
+            const uint8_t* typed_data = getData<uint8_t>();
+            for (size_t i = 0; i < cappedCount; ++i) {
+                output.appendFormat("%u ", typed_data[i]);
+            }
+            break;
+        }
+        case ASCII: {
+            const char* typed_data = reinterpret_cast<const char*>(getData<uint8_t>());
+            size_t len = count;
+            if (count > MAX_PRINT_STRING_LENGTH) {
+                 len = MAX_PRINT_STRING_LENGTH;
+            }
+            output.append(typed_data, len);
+            break;
+        }
+        case SHORT: {
+            const uint16_t* typed_data = getData<uint16_t>();
+            for (size_t i = 0; i < cappedCount; ++i) {
+                output.appendFormat("%u ", typed_data[i]);
+            }
+            break;
+        }
+        case LONG: {
+            const uint32_t* typed_data = getData<uint32_t>();
+            for (size_t i = 0; i < cappedCount; ++i) {
+                output.appendFormat("%u ", typed_data[i]);
+            }
+            break;
+        }
+        case RATIONAL: {
+            const uint32_t* typed_data = getData<uint32_t>();
+            cappedCount <<= 1;
+            for (size_t i = 0; i < cappedCount; i+=2) {
+                output.appendFormat("%u/%u ", typed_data[i], typed_data[i + 1]);
+            }
+            break;
+        }
+        case SBYTE: {
+            const int8_t* typed_data = getData<int8_t>();
+            for (size_t i = 0; i < cappedCount; ++i) {
+                output.appendFormat("%d ", typed_data[i]);
+            }
+            break;
+        }
+        case SSHORT: {
+            const int16_t* typed_data = getData<int16_t>();
+            for (size_t i = 0; i < cappedCount; ++i) {
+                output.appendFormat("%d ", typed_data[i]);
+            }
+            break;
+        }
+        case SLONG: {
+            const int32_t* typed_data = getData<int32_t>();
+            for (size_t i = 0; i < cappedCount; ++i) {
+                output.appendFormat("%d ", typed_data[i]);
+            }
+            break;
+        }
+        case SRATIONAL: {
+            const int32_t* typed_data = getData<int32_t>();
+            cappedCount <<= 1;
+            for (size_t i = 0; i < cappedCount; i+=2) {
+                output.appendFormat("%d/%d ", typed_data[i], typed_data[i + 1]);
+            }
+            break;
+        }
+        case FLOAT:
+        case DOUBLE: {
+            const float* typed_data = getData<float>();
+            for (size_t i = 0; i < cappedCount; ++i) {
+                output.appendFormat("%f ", typed_data[i]);
+            }
+            break;
+        }
+        default: {
+            output.append("unknown type ");
+            break;
+        }
+    }
+
+    if (count > MAX_PRINT_STRING_LENGTH) {
+        output.append("...");
+    }
+    output.append("']");
+    return output;
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/TiffEntryImpl.cpp b/media/img_utils/src/TiffEntryImpl.cpp
new file mode 100644
index 0000000..6efa458
--- /dev/null
+++ b/media/img_utils/src/TiffEntryImpl.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 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 <img_utils/TiffEntryImpl.h>
+#include <img_utils/TiffIfd.h>
+
+#include <utils/Vector.h>
+
+namespace android {
+namespace img_utils {
+
+template<>
+size_t TiffEntryImpl<TiffIfd>::getSize() const {
+    uint32_t total = 0;
+    for (uint32_t i = 0; i < mCount; ++i) {
+        total += mData[i].getSize();
+    }
+    return total;
+}
+
+template<>
+status_t TiffEntryImpl<TiffIfd>::writeData(uint32_t offset, EndianOutput* out) const {
+    status_t ret = OK;
+    for (uint32_t i = 0; i < mCount; ++i) {
+        BAIL_ON_FAIL(mData[i].writeData(offset, out), ret);
+    }
+    return ret;
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/TiffIfd.cpp b/media/img_utils/src/TiffIfd.cpp
new file mode 100644
index 0000000..1b3b40d
--- /dev/null
+++ b/media/img_utils/src/TiffIfd.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2014 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 <img_utils/TiffIfd.h>
+#include <img_utils/TiffHelpers.h>
+
+#include <utils/Log.h>
+
+namespace android {
+namespace img_utils {
+
+TiffIfd::TiffIfd(uint32_t ifdId)
+        : mNextIfd(), mIfdId(ifdId) {}
+
+TiffIfd::~TiffIfd() {}
+
+status_t TiffIfd::addEntry(const sp<TiffEntry>& entry) {
+    size_t size = mEntries.size();
+    if (size >= MAX_IFD_ENTRIES) {
+        ALOGW("%s: Failed to add entry for tag 0x%x to IFD %u, too many entries in IFD!",
+                __FUNCTION__, entry->getTag(), mIfdId);
+        return BAD_INDEX;
+    }
+
+    if (mEntries.add(entry) < 0) {
+        ALOGW("%s: Failed to add entry for tag 0x%x to ifd %u.", __FUNCTION__, entry->getTag(),
+                mIfdId);
+        return BAD_INDEX;
+    }
+    return OK;
+}
+
+sp<TiffEntry> TiffIfd::getEntry(uint16_t tag) const {
+    ssize_t index = mEntries.indexOfTag(tag);
+    if (index < 0) {
+        ALOGW("%s: No entry for tag 0x%x in ifd %u.", __FUNCTION__, tag, mIfdId);
+        return NULL;
+    }
+    return mEntries[index];
+}
+
+void TiffIfd::setNextIfd(const sp<TiffIfd>& ifd) {
+    mNextIfd = ifd;
+}
+
+sp<TiffIfd> TiffIfd::getNextIfd() const {
+    return mNextIfd;
+}
+
+uint32_t TiffIfd::checkAndGetOffset(uint32_t offset) const {
+    size_t size = mEntries.size();
+
+    if (size > MAX_IFD_ENTRIES) {
+        ALOGW("%s: Could not calculate IFD offsets, IFD %u contains too many entries.",
+                __FUNCTION__, mIfdId);
+        return BAD_OFFSET;
+    }
+
+    if (size <= 0) {
+        ALOGW("%s: Could not calculate IFD offsets, IFD %u contains no entries.", __FUNCTION__,
+                mIfdId);
+        return BAD_OFFSET;
+    }
+
+    if (offset == BAD_OFFSET) {
+        ALOGW("%s: Could not calculate IFD offsets, IFD %u had a bad initial offset.",
+                __FUNCTION__, mIfdId);
+        return BAD_OFFSET;
+    }
+
+    uint32_t ifdSize = calculateIfdSize(size);
+    WORD_ALIGN(ifdSize);
+    return offset + ifdSize;
+}
+
+status_t TiffIfd::writeData(uint32_t offset, /*out*/EndianOutput* out) const {
+    assert((offset % TIFF_WORD_SIZE) == 0);
+    status_t ret = OK;
+
+    ALOGV("%s: IFD %u written to offset %u", __FUNCTION__, mIfdId, offset );
+    uint32_t valueOffset = checkAndGetOffset(offset);
+    if (valueOffset == 0) {
+        return BAD_VALUE;
+    }
+
+    size_t size = mEntries.size();
+
+    // Writer IFD header (2 bytes, number of entries).
+    uint16_t header = static_cast<uint16_t>(size);
+    BAIL_ON_FAIL(out->write(&header, 0, 1), ret);
+
+    // Write tag entries
+    for (size_t i = 0; i < size; ++i) {
+        BAIL_ON_FAIL(mEntries[i]->writeTagInfo(valueOffset, out), ret);
+        valueOffset += mEntries[i]->getSize();
+    }
+
+    // Writer IFD footer (4 bytes, offset to next IFD).
+    uint32_t footer = (mNextIfd != NULL) ? offset + getSize() : 0;
+    BAIL_ON_FAIL(out->write(&footer, 0, 1), ret);
+
+    assert(out->getCurrentOffset() == offset + calculateIfdSize(size));
+
+    // Write zeroes till word aligned
+    ZERO_TILL_WORD(out, calculateIfdSize(size), ret);
+
+    // Write values for each tag entry
+    for (size_t i = 0; i < size; ++i) {
+        size_t last = out->getCurrentOffset();
+        // Only write values that are too large to fit in the 12-byte TIFF entry
+        if (mEntries[i]->getSize() > OFFSET_SIZE) {
+            BAIL_ON_FAIL(mEntries[i]->writeData(out->getCurrentOffset(), out), ret);
+        }
+        size_t next = out->getCurrentOffset();
+        size_t diff = (next - last);
+        size_t actual = mEntries[i]->getSize();
+        if (diff != actual) {
+            ALOGW("Sizes do not match for tag %x. Expected %zu, received %zu",
+                    mEntries[i]->getTag(), actual, diff);
+        }
+    }
+
+    assert(out->getCurrentOffset() == offset + getSize());
+
+    return ret;
+}
+
+size_t TiffIfd::getSize() const {
+    size_t size = mEntries.size();
+    uint32_t total = calculateIfdSize(size);
+    WORD_ALIGN(total);
+    for (size_t i = 0; i < size; ++i) {
+        total += mEntries[i]->getSize();
+    }
+    return total;
+}
+
+uint32_t TiffIfd::getId() const {
+    return mIfdId;
+}
+
+uint32_t TiffIfd::getComparableValue() const {
+    return mIfdId;
+}
+
+String8 TiffIfd::toString() const {
+    size_t s = mEntries.size();
+    String8 output;
+    output.appendFormat("[ifd: %x, num_entries: %zu, entries:\n", getId(), s);
+    for(size_t i = 0; i < mEntries.size(); ++i) {
+        output.append("\t");
+        output.append(mEntries[i]->toString());
+        output.append("\n");
+    }
+    output.append(", next_ifd: %x]", ((mNextIfd != NULL) ? mNextIfd->getId() : 0));
+    return output;
+}
+
+void TiffIfd::log() const {
+    size_t s = mEntries.size();
+    ALOGI("[ifd: %x, num_entries: %zu, entries:\n", getId(), s);
+    for(size_t i = 0; i < s; ++i) {
+        ALOGI("\t%s", mEntries[i]->toString().string());
+    }
+    ALOGI(", next_ifd: %x]", ((mNextIfd != NULL) ? mNextIfd->getId() : 0));
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/TiffWritable.cpp b/media/img_utils/src/TiffWritable.cpp
new file mode 100644
index 0000000..f8d7de7
--- /dev/null
+++ b/media/img_utils/src/TiffWritable.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 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 <img_utils/TiffWritable.h>
+#include <img_utils/TiffHelpers.h>
+
+#include <assert.h>
+
+namespace android {
+namespace img_utils {
+
+TiffWritable::TiffWritable() {}
+
+TiffWritable::~TiffWritable() {}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/img_utils/src/TiffWriter.cpp b/media/img_utils/src/TiffWriter.cpp
new file mode 100644
index 0000000..2439033
--- /dev/null
+++ b/media/img_utils/src/TiffWriter.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2014 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 <img_utils/TiffIfd.h>
+#include <img_utils/TiffHelpers.h>
+#include <img_utils/TiffWriter.h>
+#include <img_utils/TagDefinitions.h>
+
+#include <assert.h>
+
+namespace android {
+namespace img_utils {
+
+KeyedVector<uint16_t, const TagDefinition_t*> TiffWriter::buildTagMap(
+            const TagDefinition_t* definitions, size_t length) {
+    KeyedVector<uint16_t, const TagDefinition_t*> map;
+    for(size_t i = 0; i < length; ++i) {
+        map.add(definitions[i].tagId, definitions + i);
+    }
+    return map;
+}
+
+#define COMPARE(op) \
+bool Orderable::operator op (const Orderable& orderable) const { \
+    return getComparableValue() op orderable.getComparableValue(); \
+}
+
+#define ARRAY_SIZE(array) \
+    (sizeof(array) / sizeof(array[0]))
+
+KeyedVector<uint16_t, const TagDefinition_t*> TiffWriter::sTagMaps[] = {
+    buildTagMap(TIFF_EP_TAG_DEFINITIONS, ARRAY_SIZE(TIFF_EP_TAG_DEFINITIONS)),
+    buildTagMap(DNG_TAG_DEFINITIONS, ARRAY_SIZE(DNG_TAG_DEFINITIONS)),
+    buildTagMap(EXIF_2_3_TAG_DEFINITIONS, ARRAY_SIZE(EXIF_2_3_TAG_DEFINITIONS)),
+    buildTagMap(TIFF_6_TAG_DEFINITIONS, ARRAY_SIZE(TIFF_6_TAG_DEFINITIONS))
+};
+
+TiffWriter::TiffWriter() : mTagMaps(sTagMaps), mNumTagMaps(DEFAULT_NUM_TAG_MAPS) {}
+
+TiffWriter::TiffWriter(KeyedVector<uint16_t, const TagDefinition_t*>* enabledDefinitions,
+        size_t length) : mTagMaps(enabledDefinitions), mNumTagMaps(length) {}
+
+TiffWriter::~TiffWriter() {}
+
+status_t TiffWriter::write(Output* out, Endianness end) {
+    status_t ret = OK;
+    EndianOutput endOut(out, end);
+
+    if (mIfd == NULL) {
+        ALOGE("%s: Tiff header is empty.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+    BAIL_ON_FAIL(writeFileHeader(endOut), ret);
+
+    uint32_t offset = FILE_HEADER_SIZE;
+    sp<TiffIfd> ifd = mIfd;
+    while(ifd != NULL) {
+        BAIL_ON_FAIL(ifd->writeData(offset, &endOut), ret);
+        offset += ifd->getSize();
+        ifd = ifd->getNextIfd();
+    }
+    return ret;
+}
+
+
+const TagDefinition_t* TiffWriter::lookupDefinition(uint16_t tag) const {
+    const TagDefinition_t* definition = NULL;
+    for (size_t i = 0; i < mNumTagMaps; ++i) {
+        ssize_t index = mTagMaps[i].indexOfKey(tag);
+        if (index >= 0) {
+            definition = mTagMaps[i][index];
+            break;
+        }
+    }
+
+    if (definition == NULL) {
+        ALOGE("%s: No definition exists for tag with id %x.", __FUNCTION__, tag);
+    }
+    return definition;
+}
+
+sp<TiffEntry> TiffWriter::getEntry(uint16_t tag, uint32_t ifd) const {
+    ssize_t index = mNamedIfds.indexOfKey(ifd);
+    if (index < 0) {
+        ALOGE("%s: No IFD %d set for this writer.", __FUNCTION__, ifd);
+        return NULL;
+    }
+    return mNamedIfds[index]->getEntry(tag);
+}
+
+
+// TODO: Fix this to handle IFD position in chain/sub-IFD tree
+status_t TiffWriter::addEntry(const sp<TiffEntry>& entry) {
+    uint16_t tag = entry->getTag();
+
+    const TagDefinition_t* definition = lookupDefinition(tag);
+
+    if (definition == NULL) {
+        return BAD_INDEX;
+    }
+    uint32_t ifdId = 0;  // TODO: all in IFD0 for now.
+
+    ssize_t index = mNamedIfds.indexOfKey(ifdId);
+
+    // Add a new IFD if necessary
+    if (index < 0) {
+        sp<TiffIfd> ifdEntry = new TiffIfd(ifdId);
+        if (mIfd == NULL) {
+            mIfd = ifdEntry;
+        }
+        index = mNamedIfds.add(ifdId, ifdEntry);
+        assert(index >= 0);
+    }
+
+    sp<TiffIfd> selectedIfd  = mNamedIfds[index];
+    return selectedIfd->addEntry(entry);
+}
+
+status_t TiffWriter::uncheckedAddIfd(const sp<TiffIfd>& ifd) {
+    mNamedIfds.add(ifd->getId(), ifd);
+    sp<TiffIfd> last = findLastIfd();
+    if (last == NULL) {
+        mIfd = ifd;
+    } else {
+        last->setNextIfd(ifd);
+    }
+    last = ifd->getNextIfd();
+    while (last != NULL) {
+        mNamedIfds.add(last->getId(), last);
+        last = last->getNextIfd();
+    }
+    return OK;
+}
+
+status_t TiffWriter::addIfd(uint32_t ifd) {
+    ssize_t index = mNamedIfds.indexOfKey(ifd);
+    if (index >= 0) {
+        ALOGE("%s: Ifd with ID 0x%x already exists.", __FUNCTION__, ifd);
+        return BAD_VALUE;
+    }
+    sp<TiffIfd> newIfd = new TiffIfd(ifd);
+    if (mIfd == NULL) {
+        mIfd = newIfd;
+    } else {
+        sp<TiffIfd> last = findLastIfd();
+        last->setNextIfd(newIfd);
+    }
+    mNamedIfds.add(ifd, newIfd);
+    return OK;
+}
+
+TagType TiffWriter::getDefaultType(uint16_t tag) const {
+    const TagDefinition_t* definition = lookupDefinition(tag);
+    if (definition == NULL) {
+        ALOGE("%s: Could not find definition for tag %x", __FUNCTION__, tag);
+        return UNKNOWN_TAGTYPE;
+    }
+    return definition->defaultType;
+}
+
+uint32_t TiffWriter::getDefaultCount(uint16_t tag) const {
+    const TagDefinition_t* definition = lookupDefinition(tag);
+    if (definition == NULL) {
+        ALOGE("%s: Could not find definition for tag %x", __FUNCTION__, tag);
+        return 0;
+    }
+    return definition->fixedCount;
+}
+
+bool TiffWriter::checkIfDefined(uint16_t tag) const {
+    return lookupDefinition(tag) != NULL;
+}
+
+sp<TiffIfd> TiffWriter::findLastIfd() {
+    sp<TiffIfd> ifd = mIfd;
+    while(ifd != NULL) {
+        sp<TiffIfd> nextIfd = ifd->getNextIfd();
+        if (nextIfd == NULL) {
+            break;
+        }
+        ifd = nextIfd;
+    }
+    return ifd;
+}
+
+status_t TiffWriter::writeFileHeader(EndianOutput& out) {
+    status_t ret = OK;
+    uint16_t endMarker = (out.getEndianness() == BIG) ? BIG_ENDIAN_MARKER : LITTLE_ENDIAN_MARKER;
+    BAIL_ON_FAIL(out.write(&endMarker, 0, 1), ret);
+
+    uint16_t tiffMarker = TIFF_FILE_MARKER;
+    BAIL_ON_FAIL(out.write(&tiffMarker, 0, 1), ret);
+
+    uint32_t offsetMarker = FILE_HEADER_SIZE;
+    BAIL_ON_FAIL(out.write(&offsetMarker, 0, 1), ret);
+    return ret;
+}
+
+uint32_t TiffWriter::getTotalSize() const {
+    uint32_t totalSize = FILE_HEADER_SIZE;
+    sp<TiffIfd> ifd = mIfd;
+    while(ifd != NULL) {
+        totalSize += ifd->getSize();
+        ifd = ifd->getNextIfd();
+    }
+    return totalSize;
+}
+
+void TiffWriter::log() const {
+    ALOGI("%s: TiffWriter:", __FUNCTION__);
+    sp<TiffIfd> ifd = mIfd;
+    while(ifd != NULL) {
+        ifd->log();
+        ifd = ifd->getNextIfd();
+    }
+}
+
+} /*namespace img_utils*/
+} /*namespace android*/
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 1c808d0..db61e85 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -203,23 +203,6 @@
         mFrameSize = sizeof(uint8_t);
     }
 
-    // validate framecount
-    size_t minFrameCount;
-    status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
-            sampleRate, format, channelMask);
-    if (status != NO_ERROR) {
-        ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; status %d",
-                sampleRate, format, channelMask, status);
-        return status;
-    }
-    ALOGV("AudioRecord::set() minFrameCount = %d", minFrameCount);
-
-    if (frameCount == 0) {
-        frameCount = minFrameCount;
-    } else if (frameCount < minFrameCount) {
-        ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
-        return BAD_VALUE;
-    }
     // mFrameCount is initialized in openRecord_l
     mReqFrameCount = frameCount;
 
@@ -242,7 +225,7 @@
     }
 
     // create the IAudioRecord
-    status = openRecord_l(0 /*epoch*/);
+    status_t status = openRecord_l(0 /*epoch*/);
 
     if (status != NO_ERROR) {
         if (mAudioRecordThread != 0) {
@@ -464,6 +447,29 @@
     size_t frameCount = mReqFrameCount;
 
     if (!(mFlags & AUDIO_INPUT_FLAG_FAST)) {
+        // validate framecount
+        // If fast track was not requested, this preserves
+        // the old behavior of validating on client side.
+        // FIXME Eventually the validation should be done on server side
+        // regardless of whether it's a fast or normal track.  It's debatable
+        // whether to account for the input latency to provision buffers appropriately.
+        size_t minFrameCount;
+        status = AudioRecord::getMinFrameCount(&minFrameCount,
+                mSampleRate, mFormat, mChannelMask);
+        if (status != NO_ERROR) {
+            ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; "
+                    "status %d",
+                    mSampleRate, mFormat, mChannelMask, status);
+            return status;
+        }
+
+        if (frameCount == 0) {
+            frameCount = minFrameCount;
+        } else if (frameCount < minFrameCount) {
+            ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
+            return BAD_VALUE;
+        }
+
         // Make sure that application is notified with sufficient margin before overrun
         if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) {
             mNotificationFramesAct = frameCount/2;
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index b4d6d76..7d3ecc5 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -19,6 +19,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AudioTrack"
 
+#include <math.h>
 #include <sys/resource.h>
 #include <audio_utils/primitives.h>
 #include <binder/IPCThreadState.h>
@@ -566,7 +567,9 @@
 
 status_t AudioTrack::setVolume(float left, float right)
 {
-    if (left < 0.0f || left > 1.0f || right < 0.0f || right > 1.0f) {
+    // This duplicates a test by AudioTrack JNI, but that is not the only caller
+    if (isnanf(left) || left < GAIN_FLOAT_ZERO || left > GAIN_FLOAT_UNITY ||
+            isnanf(right) || right < GAIN_FLOAT_ZERO || right > GAIN_FLOAT_UNITY) {
         return BAD_VALUE;
     }
 
@@ -574,7 +577,7 @@
     mVolume[AUDIO_INTERLEAVE_LEFT] = left;
     mVolume[AUDIO_INTERLEAVE_RIGHT] = right;
 
-    mProxy->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
+    mProxy->setVolumeLR(gain_minifloat_pack(gain_from_float(left), gain_from_float(right)));
 
     if (isOffloaded_l()) {
         mAudioTrack->signal();
@@ -589,7 +592,8 @@
 
 status_t AudioTrack::setAuxEffectSendLevel(float level)
 {
-    if (level < 0.0f || level > 1.0f) {
+    // This duplicates a test by AudioTrack JNI, but that is not the only caller
+    if (isnanf(level) || level < GAIN_FLOAT_ZERO || level > GAIN_FLOAT_UNITY) {
         return BAD_VALUE;
     }
 
@@ -1137,8 +1141,7 @@
         mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
         mProxy = mStaticProxy;
     }
-    mProxy->setVolumeLR((uint32_t(uint16_t(mVolume[AUDIO_INTERLEAVE_RIGHT] * 0x1000)) << 16) |
-            uint16_t(mVolume[AUDIO_INTERLEAVE_LEFT] * 0x1000));
+    mProxy->setVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY);
     mProxy->setSendLevel(mSendLevel);
     mProxy->setSampleRate(mSampleRate);
     mProxy->setEpoch(epoch);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 323b675..219dbfd 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -19,15 +19,15 @@
 
 #include <private/media/AudioTrackShared.h>
 #include <utils/Log.h>
-extern "C" {
-#include "../private/bionic_futex.h"
-}
+
+#include <linux/futex.h>
+#include <sys/syscall.h>
 
 namespace android {
 
 audio_track_cblk_t::audio_track_cblk_t()
     : mServer(0), mFutex(0), mMinimum(0),
-    mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mFlags(0)
+    mVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY), mSampleRate(0), mSendLevel(0), mFlags(0)
 {
     memset(&u, 0, sizeof(u));
 }
@@ -206,12 +206,12 @@
         }
         int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
         if (!(old & CBLK_FUTEX_WAKE)) {
-            int rc;
             if (measure && !beforeIsValid) {
                 clock_gettime(CLOCK_MONOTONIC, &before);
                 beforeIsValid = true;
             }
-            int ret = __futex_syscall4(&cblk->mFutex,
+            errno = 0;
+            (void) syscall(__NR_futex, &cblk->mFutex,
                     mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
             // update total elapsed time spent waiting
             if (measure) {
@@ -230,16 +230,16 @@
                 before = after;
                 beforeIsValid = true;
             }
-            switch (ret) {
-            case 0:             // normal wakeup by server, or by binderDied()
-            case -EWOULDBLOCK:  // benign race condition with server
-            case -EINTR:        // wait was interrupted by signal or other spurious wakeup
-            case -ETIMEDOUT:    // time-out expired
+            switch (errno) {
+            case 0:            // normal wakeup by server, or by binderDied()
+            case EWOULDBLOCK:  // benign race condition with server
+            case EINTR:        // wait was interrupted by signal or other spurious wakeup
+            case ETIMEDOUT:    // time-out expired
                 // FIXME these error/non-0 status are being dropped
                 break;
             default:
-                ALOGE("%s unexpected error %d", __func__, ret);
-                status = -ret;
+                status = errno;
+                ALOGE("%s unexpected error %s", __func__, strerror(status));
                 goto end;
             }
         }
@@ -295,7 +295,7 @@
     audio_track_cblk_t* cblk = mCblk;
     if (!(android_atomic_or(CBLK_INVALID, &cblk->mFlags) & CBLK_INVALID)) {
         // it seems that a FUTEX_WAKE_PRIVATE will not wake a FUTEX_WAIT, even within same process
-        (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+        (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
                 1);
     }
 }
@@ -304,7 +304,7 @@
 {
     audio_track_cblk_t* cblk = mCblk;
     if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->mFlags) & CBLK_INTERRUPT)) {
-        (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+        (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
                 1);
     }
 }
@@ -435,18 +435,18 @@
         }
         int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
         if (!(old & CBLK_FUTEX_WAKE)) {
-            int rc;
-            int ret = __futex_syscall4(&cblk->mFutex,
+            errno = 0;
+            (void) syscall(__NR_futex, &cblk->mFutex,
                     mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
-            switch (ret) {
-            case 0:             // normal wakeup by server, or by binderDied()
-            case -EWOULDBLOCK:  // benign race condition with server
-            case -EINTR:        // wait was interrupted by signal or other spurious wakeup
-            case -ETIMEDOUT:    // time-out expired
+            switch (errno) {
+            case 0:            // normal wakeup by server, or by binderDied()
+            case EWOULDBLOCK:  // benign race condition with server
+            case EINTR:        // wait was interrupted by signal or other spurious wakeup
+            case ETIMEDOUT:    // time-out expired
                 break;
             default:
-                ALOGE("%s unexpected error %d", __func__, ret);
-                status = -ret;
+                status = errno;
+                ALOGE("%s unexpected error %s", __func__, strerror(status));
                 goto end;
             }
         }
@@ -535,7 +535,7 @@
             if (front != rear) {
                 int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
                 if (!(old & CBLK_FUTEX_WAKE)) {
-                    (void) __futex_syscall3(&cblk->mFutex,
+                    (void) syscall(__NR_futex, &cblk->mFutex,
                             mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
                 }
             }
@@ -638,7 +638,7 @@
         ALOGV("mAvailToClient=%u stepCount=%u minimum=%u", mAvailToClient, stepCount, minimum);
         int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
         if (!(old & CBLK_FUTEX_WAKE)) {
-            (void) __futex_syscall3(&cblk->mFutex,
+            (void) syscall(__NR_futex, &cblk->mFutex,
                     mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
         }
     }
@@ -683,7 +683,7 @@
     bool old =
             (android_atomic_or(CBLK_STREAM_END_DONE, &cblk->mFlags) & CBLK_STREAM_END_DONE) != 0;
     if (!old) {
-        (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+        (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
                 1);
     }
     return old;
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp
index 74e5013..9b239b1 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.cpp
+++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp
@@ -186,7 +186,7 @@
         read(fd, buf, sizeof(buf));
         lseek(fd, offset, SEEK_SET);
 
-        long ident = *((long*)buf);
+        uint32_t ident = *((uint32_t*)buf);
 
         // Ogg vorbis?
         if (ident == 0x5367674f) // 'OggS'
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index 4d9a1fa..4d14904 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -438,7 +438,7 @@
 void NBLog::Reader::dumpLine(const String8& timestamp, String8& body)
 {
     if (mFd >= 0) {
-        fdprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
+        dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
     } else {
         ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string());
     }
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2e8e412..8f154be 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -990,6 +990,8 @@
             "audio_decoder.g711alaw", "audio_encoder.g711alaw" },
         { MEDIA_MIMETYPE_VIDEO_AVC,
             "video_decoder.avc", "video_encoder.avc" },
+        { MEDIA_MIMETYPE_VIDEO_HEVC,
+            "video_decoder.hevc", "video_encoder.hevc" },
         { MEDIA_MIMETYPE_VIDEO_MPEG4,
             "video_decoder.mpeg4", "video_encoder.mpeg4" },
         { MEDIA_MIMETYPE_VIDEO_H263,
@@ -1811,6 +1813,7 @@
     OMX_VIDEO_CODINGTYPE mVideoCodingType;
 } kVideoCodingMapEntry[] = {
     { MEDIA_MIMETYPE_VIDEO_AVC, OMX_VIDEO_CodingAVC },
+    { MEDIA_MIMETYPE_VIDEO_HEVC, OMX_VIDEO_CodingHEVC },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, OMX_VIDEO_CodingMPEG4 },
     { MEDIA_MIMETYPE_VIDEO_H263, OMX_VIDEO_CodingH263 },
     { MEDIA_MIMETYPE_VIDEO_MPEG2, OMX_VIDEO_CodingMPEG2 },
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
index 11b80bf..8af0880 100644
--- a/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -27,7 +27,6 @@
 #include <media/stagefright/MetaData.h>
 
 #include <ui/GraphicBuffer.h>
-#include <sys/atomics.h>
 
 namespace android {
 
@@ -92,7 +91,7 @@
         return;
     }
 
-    int prevCount = __atomic_dec(&mRefCount);
+    int prevCount = __sync_fetch_and_sub(&mRefCount, 1);
     if (prevCount == 1) {
         if (mObserver == NULL) {
             delete this;
@@ -112,7 +111,7 @@
 }
 
 void MediaBuffer::add_ref() {
-    (void) __atomic_inc(&mRefCount);
+    (void) __sync_fetch_and_add(&mRefCount, 1);
 }
 
 void *MediaBuffer::data() const {
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index c670bb4..8229e55 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -23,6 +23,7 @@
 const char *MEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
 const char *MEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
 const char *MEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
+const char *MEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
 const char *MEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
 const char *MEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
 const char *MEDIA_MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
diff --git a/media/libstagefright/foundation/ANetworkSession.cpp b/media/libstagefright/foundation/ANetworkSession.cpp
index af5be70..4504c2b 100644
--- a/media/libstagefright/foundation/ANetworkSession.cpp
+++ b/media/libstagefright/foundation/ANetworkSession.cpp
@@ -623,7 +623,7 @@
     CHECK_EQ(mState, CONNECTED);
     CHECK(!mOutFragments.empty());
 
-    ssize_t n;
+    ssize_t n = -1;
     while (!mOutFragments.empty()) {
         const Fragment &frag = *mOutFragments.begin();
 
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index d3e546a..786bf0d 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -15,7 +15,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
 	libaudioflinger \
-	libaudiopolicy \
+	libaudiopolicyservice \
 	libcamera_metadata\
 	libcameraservice \
 	libmedialogservice \
@@ -35,7 +35,8 @@
     frameworks/av/services/medialog \
     frameworks/av/services/audioflinger \
     frameworks/av/services/audiopolicy \
-    frameworks/av/services/camera/libcameraservice
+    frameworks/av/services/camera/libcameraservice \
+    $(call include-path-for, audio-utils)
 
 LOCAL_MODULE:= mediaserver
 LOCAL_32_BIT_ONLY := true
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 9e2aa67..bd2541f 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -341,6 +341,13 @@
 }
 
 EXPORT
+media_status_t AMediaCodec_releaseOutputBufferAtTime(
+        AMediaCodec *mData, size_t idx, int64_t timestampNs) {
+    ALOGV("render @ %lld", timestampNs);
+    return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx, timestampNs));
+}
+
+EXPORT
 media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback, void *userdata) {
     mData->mCallback = callback;
     mData->mCallbackUserData = userdata;
@@ -351,7 +358,7 @@
         int numsubsamples;
         uint8_t key[16];
         uint8_t iv[16];
-        uint32_t mode;
+        cryptoinfo_mode_t mode;
         size_t *clearbytes;
         size_t *encryptedbytes;
 } AMediaCodecCryptoInfo;
@@ -396,7 +403,7 @@
         int numsubsamples,
         uint8_t key[16],
         uint8_t iv[16],
-        uint32_t mode,
+        cryptoinfo_mode_t mode,
         size_t *clearbytes,
         size_t *encryptedbytes) {
 
@@ -459,9 +466,9 @@
 }
 
 EXPORT
-uint32_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo* ci) {
+cryptoinfo_mode_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo* ci) {
     if (!ci) {
-        return AMEDIA_ERROR_INVALID_OBJECT;
+        return (cryptoinfo_mode_t) AMEDIA_ERROR_INVALID_OBJECT;
     }
     return ci->mode;
 }
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index f982275..a0cbb70 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -101,7 +101,7 @@
             return;
     }
 
-    (*mListener)(mObj, sessionId, ndkEventType, extra, data, dataSize);
+    (*mListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize);
 
     delete [] sessionId.ptr;
     delete [] data;
@@ -236,29 +236,35 @@
 }
 
 EXPORT
-media_status_t AMediaDrm_openSession(AMediaDrm *mObj, AMediaDrmSessionId &sessionId) {
+media_status_t AMediaDrm_openSession(AMediaDrm *mObj, AMediaDrmSessionId *sessionId) {
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!sessionId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
     Vector<uint8_t> session;
     status_t status = mObj->mDrm->openSession(session);
     if (status == OK) {
         mObj->mIds.push_front(session);
         List<idvec_t>::iterator iter = mObj->mIds.begin();
-        sessionId.ptr = iter->array();
-        sessionId.length = iter->size();
+        sessionId->ptr = iter->array();
+        sessionId->length = iter->size();
     }
     return AMEDIA_OK;
 }
 
 EXPORT
-media_status_t AMediaDrm_closeSession(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId) {
+media_status_t AMediaDrm_closeSession(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId) {
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!sessionId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
 
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, sessionId, iter)) {
+    if (!findId(mObj, *sessionId, iter)) {
         return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
     mObj->mDrm->closeSession(*iter);
@@ -267,20 +273,20 @@
 }
 
 EXPORT
-media_status_t AMediaDrm_getKeyRequest(AMediaDrm *mObj, const AMediaDrmScope &scope,
+media_status_t AMediaDrm_getKeyRequest(AMediaDrm *mObj, const AMediaDrmScope *scope,
         const uint8_t *init, size_t initSize, const char *mimeType, AMediaDrmKeyType keyType,
         const AMediaDrmKeyValue *optionalParameters, size_t numOptionalParameters,
-        const uint8_t *&keyRequest, size_t &keyRequestSize) {
+        const uint8_t **keyRequest, size_t *keyRequestSize) {
 
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
-    if (!mimeType) {
+    if (!mimeType || !scope || !keyRequest || !keyRequestSize) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, scope, iter)) {
+    if (!findId(mObj, *scope, iter)) {
         return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
 
@@ -311,25 +317,25 @@
     if (status != OK) {
         return translateStatus(status);
     } else {
-        keyRequest = mObj->mKeyRequest.array();
-        keyRequestSize = mObj->mKeyRequest.size();
+        *keyRequest = mObj->mKeyRequest.array();
+        *keyRequestSize = mObj->mKeyRequest.size();
     }
     return AMEDIA_OK;
 }
 
 EXPORT
-media_status_t AMediaDrm_provideKeyResponse(AMediaDrm *mObj, const AMediaDrmScope &scope,
-        const uint8_t *response, size_t responseSize, AMediaDrmKeySetId &keySetId) {
+media_status_t AMediaDrm_provideKeyResponse(AMediaDrm *mObj, const AMediaDrmScope *scope,
+        const uint8_t *response, size_t responseSize, AMediaDrmKeySetId *keySetId) {
 
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
-    if (!response || !responseSize) {
+    if (!scope || !response || !responseSize || !keySetId) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, scope, iter)) {
+    if (!findId(mObj, *scope, iter)) {
         return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
     Vector<uint8_t> mdResponse;
@@ -340,41 +346,47 @@
     if (status == OK) {
         mObj->mIds.push_front(mdKeySetId);
         List<idvec_t>::iterator iter = mObj->mIds.begin();
-        keySetId.ptr = iter->array();
-        keySetId.length = iter->size();
+        keySetId->ptr = iter->array();
+        keySetId->length = iter->size();
     } else {
-        keySetId.ptr = NULL;
-        keySetId.length = 0;
+        keySetId->ptr = NULL;
+        keySetId->length = 0;
     }
     return AMEDIA_OK;
 }
 
 EXPORT
-media_status_t AMediaDrm_restoreKeys(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
-        const AMediaDrmKeySetId &keySetId) {
+media_status_t AMediaDrm_restoreKeys(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
+        const AMediaDrmKeySetId *keySetId) {
 
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!sessionId || !keySetId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, sessionId, iter)) {
+    if (!findId(mObj, *sessionId, iter)) {
         return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
     Vector<uint8_t> keySet;
-    keySet.appendArray(keySetId.ptr, keySetId.length);
+    keySet.appendArray(keySetId->ptr, keySetId->length);
     return translateStatus(mObj->mDrm->restoreKeys(*iter, keySet));
 }
 
 EXPORT
-media_status_t AMediaDrm_removeKeys(AMediaDrm *mObj, const AMediaDrmSessionId &keySetId) {
+media_status_t AMediaDrm_removeKeys(AMediaDrm *mObj, const AMediaDrmSessionId *keySetId) {
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!keySetId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
     List<idvec_t>::iterator iter;
     status_t status;
-    if (!findId(mObj, keySetId, iter)) {
+    if (!findId(mObj, *keySetId, iter)) {
         Vector<uint8_t> keySet;
-        keySet.appendArray(keySetId.ptr, keySetId.length);
+        keySet.appendArray(keySetId->ptr, keySetId->length);
         status = mObj->mDrm->removeKeys(keySet);
     } else {
         status = mObj->mDrm->removeKeys(*iter);
@@ -384,25 +396,28 @@
 }
 
 EXPORT
-media_status_t AMediaDrm_queryKeyStatus(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
-        AMediaDrmKeyValue *keyValuePairs, size_t &numPairs) {
+media_status_t AMediaDrm_queryKeyStatus(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
+        AMediaDrmKeyValue *keyValuePairs, size_t *numPairs) {
 
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!sessionId || !numPairs) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, sessionId, iter)) {
+    if (!findId(mObj, *sessionId, iter)) {
         return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
 
     status_t status = mObj->mDrm->queryKeyStatus(*iter, mObj->mQueryResults);
     if (status != OK) {
-        numPairs = 0;
+        *numPairs = 0;
         return translateStatus(status);
     }
 
-    if (mObj->mQueryResults.size() > numPairs) {
-        numPairs = mObj->mQueryResults.size();
+    if (mObj->mQueryResults.size() > *numPairs) {
+        *numPairs = mObj->mQueryResults.size();
         return AMEDIA_DRM_SHORT_BUFFER;
     }
 
@@ -410,17 +425,17 @@
         keyValuePairs[i].mKey = mObj->mQueryResults.keyAt(i).string();
         keyValuePairs[i].mValue = mObj->mQueryResults.keyAt(i).string();
     }
-    numPairs = mObj->mQueryResults.size();
+    *numPairs = mObj->mQueryResults.size();
     return AMEDIA_OK;
 }
 
 EXPORT
-media_status_t AMediaDrm_getProvisionRequest(AMediaDrm *mObj, const uint8_t *&provisionRequest,
-        size_t &provisionRequestSize, const char *&serverUrl) {
+media_status_t AMediaDrm_getProvisionRequest(AMediaDrm *mObj, const uint8_t **provisionRequest,
+        size_t *provisionRequestSize, const char **serverUrl) {
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
-    if (!provisionRequestSize || !serverUrl) {
+    if (!provisionRequest || !provisionRequestSize || !*provisionRequestSize || !serverUrl) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
@@ -429,9 +444,9 @@
     if (status != OK) {
         return translateStatus(status);
     } else {
-        provisionRequest = mObj->mProvisionRequest.array();
-        provisionRequestSize = mObj->mProvisionRequest.size();
-        serverUrl = mObj->mProvisionUrl.string();
+        *provisionRequest = mObj->mProvisionRequest.array();
+        *provisionRequestSize = mObj->mProvisionRequest.size();
+        *serverUrl = mObj->mProvisionUrl.string();
     }
     return AMEDIA_OK;
 }
@@ -455,17 +470,20 @@
 
 EXPORT
 media_status_t AMediaDrm_getSecureStops(AMediaDrm *mObj,
-        AMediaDrmSecureStop *secureStops, size_t &numSecureStops) {
+        AMediaDrmSecureStop *secureStops, size_t *numSecureStops) {
 
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!numSecureStops) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
     status_t status = mObj->mDrm->getSecureStops(mObj->mSecureStops);
     if (status != OK) {
-        numSecureStops = 0;
+        *numSecureStops = 0;
         return translateStatus(status);
     }
-    if (numSecureStops < mObj->mSecureStops.size()) {
+    if (*numSecureStops < mObj->mSecureStops.size()) {
         return AMEDIA_DRM_SHORT_BUFFER;
     }
     List<Vector<uint8_t> >::iterator iter = mObj->mSecureStops.begin();
@@ -476,59 +494,68 @@
         ++iter;
         ++i;
     }
-    numSecureStops = mObj->mSecureStops.size();
+    *numSecureStops = mObj->mSecureStops.size();
     return AMEDIA_OK;
 }
 
 EXPORT
 media_status_t AMediaDrm_releaseSecureStops(AMediaDrm *mObj,
-        const AMediaDrmSecureStop &ssRelease) {
+        const AMediaDrmSecureStop *ssRelease) {
 
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!ssRelease) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
 
     Vector<uint8_t> release;
-    release.appendArray(ssRelease.ptr, ssRelease.length);
+    release.appendArray(ssRelease->ptr, ssRelease->length);
     return translateStatus(mObj->mDrm->releaseSecureStops(release));
 }
 
 
 EXPORT
 media_status_t AMediaDrm_getPropertyString(AMediaDrm *mObj, const char *propertyName,
-        const char *&propertyValue) {
+        const char **propertyValue) {
 
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!propertyName || !propertyValue) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
 
     status_t status = mObj->mDrm->getPropertyString(String8(propertyName),
             mObj->mPropertyString);
 
     if (status == OK) {
-        propertyValue = mObj->mPropertyString.string();
+        *propertyValue = mObj->mPropertyString.string();
     } else {
-        propertyValue = NULL;
+        *propertyValue = NULL;
     }
     return translateStatus(status);
 }
 
 EXPORT
 media_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *mObj,
-        const char *propertyName, AMediaDrmByteArray &propertyValue) {
+        const char *propertyName, AMediaDrmByteArray *propertyValue) {
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!propertyName || !propertyValue) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
 
     status_t status = mObj->mDrm->getPropertyByteArray(String8(propertyName),
             mObj->mPropertyByteArray);
 
     if (status == OK) {
-        propertyValue.ptr = mObj->mPropertyByteArray.array();
-        propertyValue.length = mObj->mPropertyByteArray.size();
+        propertyValue->ptr = mObj->mPropertyByteArray.array();
+        propertyValue->length = mObj->mPropertyByteArray.size();
     } else {
-        propertyValue.ptr = NULL;
-        propertyValue.length = 0;
+        propertyValue->ptr = NULL;
+        propertyValue->length = 0;
     }
     return translateStatus(status);
 }
@@ -598,31 +625,40 @@
 }
 
 EXPORT
-media_status_t AMediaDrm_encrypt(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+media_status_t AMediaDrm_encrypt(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
         const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
         const uint8_t *input, uint8_t *output, size_t dataSize) {
-    return encrypt_decrypt_common(mObj, sessionId, cipherAlgorithm, keyId, iv,
+    if (!sessionId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+    return encrypt_decrypt_common(mObj, *sessionId, cipherAlgorithm, keyId, iv,
             input, output, dataSize, true);
 }
 
 EXPORT
-media_status_t AMediaDrm_decrypt(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+media_status_t AMediaDrm_decrypt(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
         const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
         const uint8_t *input, uint8_t *output, size_t dataSize) {
-    return encrypt_decrypt_common(mObj, sessionId, cipherAlgorithm, keyId, iv,
+    if (!sessionId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
+    return encrypt_decrypt_common(mObj, *sessionId, cipherAlgorithm, keyId, iv,
             input, output, dataSize, false);
 }
 
 EXPORT
-media_status_t AMediaDrm_sign(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+media_status_t AMediaDrm_sign(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
         const char *macAlgorithm, uint8_t *keyId, uint8_t *message, size_t messageSize,
         uint8_t *signature, size_t *signatureSize) {
 
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!sessionId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, sessionId, iter)) {
+    if (!findId(mObj, *sessionId, iter)) {
         return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
 
@@ -650,15 +686,18 @@
 }
 
 EXPORT
-media_status_t AMediaDrm_verify(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+media_status_t AMediaDrm_verify(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
         const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize,
         const uint8_t *signature, size_t signatureSize) {
 
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
+    if (!sessionId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
+    }
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, sessionId, iter)) {
+    if (!findId(mObj, *sessionId, iter)) {
         return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
 
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 563358f..b0a9590 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -150,6 +150,20 @@
 }
 
 EXPORT
+media_status_t AMediaExtractor_seekTo(AMediaExtractor *ex, int64_t seekPosUs, SeekMode mode) {
+    android::MediaSource::ReadOptions::SeekMode sfmode;
+    if (mode == AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC) {
+        sfmode = android::MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC;
+    } else if (mode == AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC) {
+        sfmode = android::MediaSource::ReadOptions::SEEK_CLOSEST_SYNC;
+    } else {
+        sfmode = android::MediaSource::ReadOptions::SEEK_NEXT_SYNC;
+    }
+
+    return translate_error(ex->mImpl->seekTo(seekPosUs, sfmode));
+}
+
+EXPORT
 ssize_t AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) {
     //ALOGV("readSampleData");
     sp<ABuffer> tmp = new ABuffer(buffer, capacity);
@@ -331,7 +345,7 @@
             numSubSamples,
             (uint8_t*) key,
             (uint8_t*) iv,
-            mode,
+            (cryptoinfo_mode_t) mode,
             (size_t*) cleardata,
             (size_t*) crypteddata);
 }
diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp
index 19b9fc4..50fc336 100644
--- a/media/ndk/NdkMediaMuxer.cpp
+++ b/media/ndk/NdkMediaMuxer.cpp
@@ -96,10 +96,10 @@
 
 EXPORT
 media_status_t AMediaMuxer_writeSampleData(AMediaMuxer *muxer,
-        size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo &info) {
-    sp<ABuffer> buf = new ABuffer((void*)(data + info.offset), info.size);
+        size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo *info) {
+    sp<ABuffer> buf = new ABuffer((void*)(data + info->offset), info->size);
     return translate_error(
-            muxer->mImpl->writeSampleData(buf, trackIdx, info.presentationTimeUs, info.flags));
+            muxer->mImpl->writeSampleData(buf, trackIdx, info->presentationTimeUs, info->flags));
 }
 
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 45e17f8..9bd0e9b 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -143,7 +143,7 @@
     if (rc) {
         goto out;
     }
-    if ((*dev)->common.version != AUDIO_DEVICE_API_VERSION_CURRENT) {
+    if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) {
         ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version);
         rc = BAD_VALUE;
         goto out;
@@ -427,7 +427,7 @@
         if (mLogMemoryDealer != 0) {
             sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
             if (binder != 0) {
-                fdprintf(fd, "\nmedia.log:\n");
+                dprintf(fd, "\nmedia.log:\n");
                 Vector<String16> args;
                 binder->dump(fd, args);
             }
@@ -635,8 +635,12 @@
     if (lStatus != NO_ERROR) {
         // remove local strong reference to Client before deleting the Track so that the
         // Client destructor is called by the TrackBase destructor with mClientLock held
-        Mutex::Autolock _cl(mClientLock);
-        client.clear();
+        // Don't hold mClientLock when releasing the reference on the track as the
+        // destructor will acquire it.
+        {
+            Mutex::Autolock _cl(mClientLock);
+            client.clear();
+        }
         track.clear();
         goto Exit;
     }
@@ -1173,7 +1177,7 @@
     }
 
     // mClientLock should not be held here because ThreadBase::sendIoConfigEvent() will lock the
-    // ThreadBase mutex and teh locknig order is ThreadBase::mLock then AudioFlinger::mClientLock.
+    // ThreadBase mutex and the locking order is ThreadBase::mLock then AudioFlinger::mClientLock.
     if (clientAdded) {
         // the config change is always sent from playback or record threads to avoid deadlock
         // with AudioSystem::gLock
@@ -1419,8 +1423,12 @@
     if (lStatus != NO_ERROR) {
         // remove local strong reference to Client before deleting the RecordTrack so that the
         // Client destructor is called by the TrackBase destructor with mClientLock held
-        Mutex::Autolock _cl(mClientLock);
-        client.clear();
+        // Don't hold mClientLock when releasing the reference on the track as the
+        // destructor will acquire it.
+        {
+            Mutex::Autolock _cl(mClientLock);
+            client.clear();
+        }
         recordTrack.clear();
         goto Exit;
     }
@@ -2380,6 +2388,11 @@
         if (handle != 0 && id != NULL) {
             *id = handle->id();
         }
+        if (handle == 0) {
+            // remove local strong reference to Client with mClientLock held
+            Mutex::Autolock _cl(mClientLock);
+            client.clear();
+        }
     }
 
 Exit:
@@ -2590,7 +2603,7 @@
             }
         } else {
             if (fd >= 0) {
-                fdprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno));
+                dprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno));
             }
         }
         char teeTime[16];
@@ -2644,11 +2657,11 @@
             write(teeFd, &temp, sizeof(temp));
             close(teeFd);
             if (fd >= 0) {
-                fdprintf(fd, "tee copied to %s\n", teePath);
+                dprintf(fd, "tee copied to %s\n", teePath);
             }
         } else {
             if (fd >= 0) {
-                fdprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
+                dprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
             }
         }
     }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index d69d6a2..d2ded9a 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -82,9 +82,6 @@
 
 static const nsecs_t kDefaultStandbyTimeInNsecs = seconds(3);
 
-#define MAX_GAIN 4096.0f
-#define MAX_GAIN_INT 0x1000
-
 #define INCLUDING_FROM_AUDIOFLINGER_H
 
 class AudioFlinger :
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index eca9848..573ba96 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -30,6 +30,9 @@
 #include <system/audio.h>
 #include <media/nbaio/NBLog.h>
 
+// FIXME This is actually unity gain, which might not be max in future, expressed in U.12
+#define MAX_GAIN_INT AudioMixer::UNITY_GAIN
+
 namespace android {
 
 // ----------------------------------------------------------------------------
@@ -91,6 +94,7 @@
         REMOVE          = 0x4102, // Remove the sample rate converter on this track name;
                                   // the track is restored to the mix sample rate.
         // for target RAMP_VOLUME and VOLUME (8 channels max)
+        // FIXME use float for these 3 to improve the dynamic range
         VOLUME0         = 0x4200,
         VOLUME1         = 0x4201,
         AUXLEVEL        = 0x4210,
diff --git a/services/audioflinger/AudioWatchdog.cpp b/services/audioflinger/AudioWatchdog.cpp
index 93d185e..877e776 100644
--- a/services/audioflinger/AudioWatchdog.cpp
+++ b/services/audioflinger/AudioWatchdog.cpp
@@ -34,7 +34,7 @@
     } else {
         strcpy(buf, "N/A\n");
     }
-    fdprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s",
+    dprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s",
             mUnderruns, mLogs, buf);
 }
 
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index ccc4825..4170fd4 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -270,6 +270,7 @@
     sp<EffectModule> getEffectFromDesc_l(effect_descriptor_t *descriptor);
     sp<EffectModule> getEffectFromId_l(int id);
     sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type);
+    // FIXME use float to improve the dynamic range
     bool setVolume_l(uint32_t *left, uint32_t *right);
     void setDevice_l(audio_devices_t device);
     void setMode_l(audio_mode_t mode);
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index f7cc8aa..f0ce902 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -26,7 +26,6 @@
 #define ATRACE_TAG ATRACE_TAG_AUDIO
 
 #include "Configuration.h"
-#include <sys/atomics.h>
 #include <time.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
@@ -256,9 +255,9 @@
                     mixer->setBufferProvider(name, bufferProvider);
                     if (fastTrack->mVolumeProvider == NULL) {
                         mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0,
-                                (void *)0x1000);
+                                (void *) MAX_GAIN_INT);
                         mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
-                                (void *)0x1000);
+                                (void *) MAX_GAIN_INT);
                     }
                     mixer->setParameter(name, AudioMixer::RESAMPLE,
                             AudioMixer::REMOVE, NULL);
@@ -313,11 +312,13 @@
             int name = fastTrackNames[i];
             ALOG_ASSERT(name >= 0);
             if (fastTrack->mVolumeProvider != NULL) {
-                uint32_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
+                gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
                 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0,
-                        (void *)(uintptr_t)(vlr & 0xFFFF));
+                        (void *) (uintptr_t)
+                            (float_from_gain(gain_minifloat_unpack_left(vlr)) * MAX_GAIN_INT));
                 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
-                        (void *)(uintptr_t)(vlr >> 16));
+                        (void *) (uintptr_t)
+                            (float_from_gain(gain_minifloat_unpack_right(vlr)) * MAX_GAIN_INT));
             }
             // FIXME The current implementation of framesReady() for fast tracks
             // takes a tryLock, which can block
@@ -464,7 +465,7 @@
 void FastMixerDumpState::dump(int fd) const
 {
     if (mCommand == FastMixerState::INITIAL) {
-        fdprintf(fd, "  FastMixer not initialized\n");
+        dprintf(fd, "  FastMixer not initialized\n");
         return;
     }
 #define COMMAND_MAX 32
@@ -498,10 +499,10 @@
     double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) +
             (mMeasuredWarmupTs.tv_nsec / 1000000.0);
     double mixPeriodSec = (double) mFrameCount / (double) mSampleRate;
-    fdprintf(fd, "  FastMixer command=%s writeSequence=%u framesWritten=%u\n"
-                 "            numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
-                 "            sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
-                 "            mixPeriod=%.2f ms\n",
+    dprintf(fd, "  FastMixer command=%s writeSequence=%u framesWritten=%u\n"
+                "            numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
+                "            sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
+                "            mixPeriod=%.2f ms\n",
                  string, mWriteSequence, mFramesWritten,
                  mNumTracks, mWriteErrors, mUnderruns, mOverruns,
                  mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
@@ -553,26 +554,26 @@
 #endif
     }
     if (n) {
-        fdprintf(fd, "  Simple moving statistics over last %.1f seconds:\n",
-                     wall.n() * mixPeriodSec);
-        fdprintf(fd, "    wall clock time in ms per mix cycle:\n"
-                     "      mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
-                     wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6,
-                     wall.stddev()*1e-6);
-        fdprintf(fd, "    raw CPU load in us per mix cycle:\n"
-                     "      mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
-                     loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3,
-                     loadNs.stddev()*1e-3);
+        dprintf(fd, "  Simple moving statistics over last %.1f seconds:\n",
+                    wall.n() * mixPeriodSec);
+        dprintf(fd, "    wall clock time in ms per mix cycle:\n"
+                    "      mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
+                    wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6,
+                    wall.stddev()*1e-6);
+        dprintf(fd, "    raw CPU load in us per mix cycle:\n"
+                    "      mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
+                    loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3,
+                    loadNs.stddev()*1e-3);
     } else {
-        fdprintf(fd, "  No FastMixer statistics available currently\n");
+        dprintf(fd, "  No FastMixer statistics available currently\n");
     }
 #ifdef CPU_FREQUENCY_STATISTICS
-    fdprintf(fd, "  CPU clock frequency in MHz:\n"
-                 "    mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
-                 kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3);
-    fdprintf(fd, "  adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n"
-                 "    mean=%.1f min=%.1f max=%.1f stddev=%.1f\n",
-                 loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev());
+    dprintf(fd, "  CPU clock frequency in MHz:\n"
+                "    mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
+                kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3);
+    dprintf(fd, "  adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n"
+                "    mean=%.1f min=%.1f max=%.1f stddev=%.1f\n",
+                loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev());
 #endif
     if (tail != NULL) {
         qsort(tail, n, sizeof(uint32_t), compare_uint32_t);
@@ -583,12 +584,12 @@
             left.sample(tail[i]);
             right.sample(tail[n - (i + 1)]);
         }
-        fdprintf(fd, "  Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n"
-                     "    left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n"
-                     "    right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
-                     left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6,
-                     right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6,
-                     right.stddev()*1e-6);
+        dprintf(fd, "  Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n"
+                    "    left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n"
+                    "    right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
+                    left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6,
+                    right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6,
+                    right.stddev()*1e-6);
         delete[] tail;
     }
 #endif
@@ -598,9 +599,9 @@
     // Instead we always display all tracks, with an indication
     // of whether we think the track is active.
     uint32_t trackMask = mTrackMask;
-    fdprintf(fd, "  Fast tracks: kMaxFastTracks=%u activeMask=%#x\n",
+    dprintf(fd, "  Fast tracks: kMaxFastTracks=%u activeMask=%#x\n",
             FastMixerState::kMaxFastTracks, trackMask);
-    fdprintf(fd, "  Index Active Full Partial Empty  Recent Ready\n");
+    dprintf(fd, "  Index Active Full Partial Empty  Recent Ready\n");
     for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) {
         bool isActive = trackMask & 1;
         const FastTrackDump *ftDump = &mTracks[i];
@@ -620,7 +621,7 @@
             mostRecent = "?";
             break;
         }
-        fdprintf(fd, "  %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no",
+        dprintf(fd, "  %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no",
                 (underruns.mBitFields.mFull) & UNDERRUN_MASK,
                 (underruns.mBitFields.mPartial) & UNDERRUN_MASK,
                 (underruns.mBitFields.mEmpty) & UNDERRUN_MASK,
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 981c1a7..db89ef4 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -17,13 +17,11 @@
 #ifndef ANDROID_AUDIO_FAST_MIXER_H
 #define ANDROID_AUDIO_FAST_MIXER_H
 
+#include <linux/futex.h>
+#include <sys/syscall.h>
 #include <utils/Debug.h>
-#if 1   // FIXME move to where used
-extern "C" {
-#include "../private/bionic_futex.h"
-}
-#endif
 #include "FastThread.h"
+#include <utils/Thread.h>
 #include "StateQueue.h"
 #include "FastMixerState.h"
 #include "FastMixerDumpState.h"
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index cb54ff1..661c9ca 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_AUDIO_FAST_MIXER_STATE_H
 #define ANDROID_AUDIO_FAST_MIXER_STATE_H
 
+#include <audio_utils/minifloat.h>
 #include <system/audio.h>
 #include <media/ExtendedAudioBufferProvider.h>
 #include <media/nbaio/NBAIO.h>
@@ -29,9 +30,8 @@
 
 class VolumeProvider {
 public:
-    // Return the track volume in U4_12 format: left in lower half, right in upper half. The
-    // provider implementation is responsible for validating that the return value is in range.
-    virtual uint32_t getVolumeLR() = 0;
+    // The provider implementation is responsible for validating that the return value is in range.
+    virtual gain_minifloat_packed_t getVolumeLR() = 0;
 protected:
     VolumeProvider() { }
     virtual ~VolumeProvider() { }
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index 8a216b3..216dace 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -20,10 +20,9 @@
 #define ATRACE_TAG ATRACE_TAG_AUDIO
 
 #include "Configuration.h"
+#include <linux/futex.h>
+#include <sys/syscall.h>
 #include <utils/Log.h>
-extern "C" {
-#include "../private/bionic_futex.h"
-}
 #include <utils/Trace.h>
 #include "FastThread.h"
 
@@ -157,7 +156,7 @@
                 ALOG_ASSERT(coldFutexAddr != NULL);
                 int32_t old = android_atomic_dec(coldFutexAddr);
                 if (old <= 0) {
-                    __futex_syscall4(coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
+                    syscall(__NR_futex, coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
                 }
                 int policy = sched_getscheduler(0);
                 if (!(policy == SCHED_FIFO || policy == SCHED_RR)) {
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 08b1728..6f1f293 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -65,7 +65,7 @@
             void        signal();
 
 // implement FastMixerState::VolumeProvider interface
-    virtual uint32_t    getVolumeLR();
+    virtual gain_minifloat_packed_t getVolumeLR();
 
     virtual status_t    setSyncEvent(const sp<SyncEvent>& event);
 
diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp
index 48399c0..7e01c9f 100644
--- a/services/audioflinger/StateQueue.cpp
+++ b/services/audioflinger/StateQueue.cpp
@@ -28,12 +28,12 @@
 #ifdef STATE_QUEUE_DUMP
 void StateQueueObserverDump::dump(int fd)
 {
-    fdprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
+    dprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
 }
 
 void StateQueueMutatorDump::dump(int fd)
 {
-    fdprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n",
+    dprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n",
             mPushDirty, mPushAck, mBlockedSequence);
 }
 #endif
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 0a18433..7843387 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -35,6 +35,7 @@
 #include <audio_effects/effect_aec.h>
 #include <audio_utils/primitives.h>
 #include <audio_utils/format.h>
+#include <audio_utils/minifloat.h>
 
 // NBAIO implementations
 #include <media/nbaio/AudioStreamOutSink.h>
@@ -504,30 +505,30 @@
 
     bool locked = AudioFlinger::dumpTryLock(mLock);
     if (!locked) {
-        fdprintf(fd, "thread %p maybe dead locked\n", this);
+        dprintf(fd, "thread %p maybe dead locked\n", this);
     }
 
-    fdprintf(fd, "  I/O handle: %d\n", mId);
-    fdprintf(fd, "  TID: %d\n", getTid());
-    fdprintf(fd, "  Standby: %s\n", mStandby ? "yes" : "no");
-    fdprintf(fd, "  Sample rate: %u\n", mSampleRate);
-    fdprintf(fd, "  HAL frame count: %zu\n", mFrameCount);
-    fdprintf(fd, "  HAL buffer size: %u bytes\n", mBufferSize);
-    fdprintf(fd, "  Channel Count: %u\n", mChannelCount);
-    fdprintf(fd, "  Channel Mask: 0x%08x (%s)\n", mChannelMask,
+    dprintf(fd, "  I/O handle: %d\n", mId);
+    dprintf(fd, "  TID: %d\n", getTid());
+    dprintf(fd, "  Standby: %s\n", mStandby ? "yes" : "no");
+    dprintf(fd, "  Sample rate: %u\n", mSampleRate);
+    dprintf(fd, "  HAL frame count: %zu\n", mFrameCount);
+    dprintf(fd, "  HAL buffer size: %u bytes\n", mBufferSize);
+    dprintf(fd, "  Channel Count: %u\n", mChannelCount);
+    dprintf(fd, "  Channel Mask: 0x%08x (%s)\n", mChannelMask,
             channelMaskToString(mChannelMask, mType != RECORD).string());
-    fdprintf(fd, "  Format: 0x%x (%s)\n", mFormat, formatToString(mFormat));
-    fdprintf(fd, "  Frame size: %zu\n", mFrameSize);
-    fdprintf(fd, "  Pending config events:");
+    dprintf(fd, "  Format: 0x%x (%s)\n", mFormat, formatToString(mFormat));
+    dprintf(fd, "  Frame size: %zu\n", mFrameSize);
+    dprintf(fd, "  Pending config events:");
     size_t numConfig = mConfigEvents.size();
     if (numConfig) {
         for (size_t i = 0; i < numConfig; i++) {
             mConfigEvents[i]->dump(buffer, SIZE);
-            fdprintf(fd, "\n    %s", buffer);
+            dprintf(fd, "\n    %s", buffer);
         }
-        fdprintf(fd, "\n");
+        dprintf(fd, "\n");
     } else {
-        fdprintf(fd, " none\n");
+        dprintf(fd, " none\n");
     }
 
     if (locked) {
@@ -1190,15 +1191,15 @@
 
     // These values are "raw"; they will wrap around.  See prepareTracks_l() for a better way.
     FastTrackUnderruns underruns = getFastTrackUnderruns(0);
-    fdprintf(fd, "  Normal mixer raw underrun counters: partial=%u empty=%u\n",
+    dprintf(fd, "  Normal mixer raw underrun counters: partial=%u empty=%u\n",
             underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty);
 
     size_t numtracks = mTracks.size();
     size_t numactive = mActiveTracks.size();
-    fdprintf(fd, "  %d Tracks", numtracks);
+    dprintf(fd, "  %d Tracks", numtracks);
     size_t numactiveseen = 0;
     if (numtracks) {
-        fdprintf(fd, " of which %d are active\n", numactive);
+        dprintf(fd, " of which %d are active\n", numactive);
         Track::appendDumpHeader(result);
         for (size_t i = 0; i < numtracks; ++i) {
             sp<Track> track = mTracks[i];
@@ -1230,22 +1231,21 @@
     }
 
     write(fd, result.string(), result.size());
-
 }
 
 void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
 {
-    fdprintf(fd, "\nOutput thread %p:\n", this);
-    fdprintf(fd, "  Normal frame count: %zu\n", mNormalFrameCount);
-    fdprintf(fd, "  Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
-    fdprintf(fd, "  Total writes: %d\n", mNumWrites);
-    fdprintf(fd, "  Delayed writes: %d\n", mNumDelayedWrites);
-    fdprintf(fd, "  Blocked in write: %s\n", mInWrite ? "yes" : "no");
-    fdprintf(fd, "  Suspend count: %d\n", mSuspended);
-    fdprintf(fd, "  Sink buffer : %p\n", mSinkBuffer);
-    fdprintf(fd, "  Mixer buffer: %p\n", mMixerBuffer);
-    fdprintf(fd, "  Effect buffer: %p\n", mEffectBuffer);
-    fdprintf(fd, "  Fast track availMask=%#x\n", mFastTrackAvailMask);
+    dprintf(fd, "\nOutput thread %p:\n", this);
+    dprintf(fd, "  Normal frame count: %zu\n", mNormalFrameCount);
+    dprintf(fd, "  Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
+    dprintf(fd, "  Total writes: %d\n", mNumWrites);
+    dprintf(fd, "  Delayed writes: %d\n", mNumDelayedWrites);
+    dprintf(fd, "  Blocked in write: %s\n", mInWrite ? "yes" : "no");
+    dprintf(fd, "  Suspend count: %d\n", mSuspended);
+    dprintf(fd, "  Sink buffer : %p\n", mSinkBuffer);
+    dprintf(fd, "  Mixer buffer: %p\n", mMixerBuffer);
+    dprintf(fd, "  Effect buffer: %p\n", mEffectBuffer);
+    dprintf(fd, "  Fast track availMask=%#x\n", mFastTrackAvailMask);
 
     dumpBase(fd, args);
 }
@@ -2753,7 +2753,7 @@
         if (state->mCommand == FastMixerState::COLD_IDLE) {
             int32_t old = android_atomic_inc(&mFastMixerFutex);
             if (old == -1) {
-                (void) __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
+                (void) syscall(__NR_futex, &mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
             }
         }
         state->mCommand = FastMixerState::EXIT;
@@ -2810,7 +2810,7 @@
             if (state->mCommand == FastMixerState::COLD_IDLE) {
                 int32_t old = android_atomic_inc(&mFastMixerFutex);
                 if (old == -1) {
-                    (void) __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
+                    (void) syscall(__NR_futex, &mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
                 }
 #ifdef AUDIO_WATCHDOG
                 if (mAudioWatchdog != 0) {
@@ -3258,21 +3258,23 @@
                 float typeVolume = mStreamTypes[track->streamType()].volume;
                 float v = masterVolume * typeVolume;
                 AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
-                uint32_t vlr = proxy->getVolumeLR();
-                vl = vlr & 0xFFFF;
-                vr = vlr >> 16;
+                gain_minifloat_packed_t vlr = proxy->getVolumeLR();
+                float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
+                float vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
                 // track volumes come from shared memory, so can't be trusted and must be clamped
-                if (vl > MAX_GAIN_INT) {
-                    ALOGV("Track left volume out of range: %04X", vl);
-                    vl = MAX_GAIN_INT;
+                if (vlf > GAIN_FLOAT_UNITY) {
+                    ALOGV("Track left volume out of range: %.3g", vlf);
+                    vlf = GAIN_FLOAT_UNITY;
                 }
-                if (vr > MAX_GAIN_INT) {
-                    ALOGV("Track right volume out of range: %04X", vr);
-                    vr = MAX_GAIN_INT;
+                if (vrf > GAIN_FLOAT_UNITY) {
+                    ALOGV("Track right volume out of range: %.3g", vrf);
+                    vrf = GAIN_FLOAT_UNITY;
                 }
                 // now apply the master volume and stream type volume
-                vl = (uint32_t)(v * vl) << 12;
-                vr = (uint32_t)(v * vr) << 12;
+                // FIXME we're losing the wonderful dynamic range in the minifloat representation
+                float v8_24 = v * (MAX_GAIN_INT * MAX_GAIN_INT);
+                vl = (uint32_t) (v8_24 * vlf);
+                vr = (uint32_t) (v8_24 * vrf);
                 // assuming master volume and stream type volume each go up to 1.0,
                 // vl and vr are now in 8.24 format
 
@@ -3299,6 +3301,7 @@
                 track->mHasVolumeController = false;
             }
 
+            // FIXME Use float
             // Convert volumes from 8.24 to 4.12 format
             // This additional clamping is needed in case chain->setVolume_l() overshot
             vl = (vl + (1 << 11)) >> 12;
@@ -3674,7 +3677,7 @@
 
     PlaybackThread::dumpInternals(fd, args);
 
-    fdprintf(fd, "  AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames());
+    dprintf(fd, "  AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames());
 
     // Make a non-atomic copy of fast mixer dump state so it won't change underneath us
     const FastMixerDumpState copy(mFastMixerDumpState);
@@ -3755,13 +3758,17 @@
         float typeVolume = mStreamTypes[track->streamType()].volume;
         float v = mMasterVolume * typeVolume;
         AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
-        uint32_t vlr = proxy->getVolumeLR();
-        float v_clamped = v * (vlr & 0xFFFF);
-        if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
-        left = v_clamped/MAX_GAIN;
-        v_clamped = v * (vlr >> 16);
-        if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
-        right = v_clamped/MAX_GAIN;
+        gain_minifloat_packed_t vlr = proxy->getVolumeLR();
+        left = float_from_gain(gain_minifloat_unpack_left(vlr));
+        if (left > GAIN_FLOAT_UNITY) {
+            left = GAIN_FLOAT_UNITY;
+        }
+        left *= v;
+        right = float_from_gain(gain_minifloat_unpack_right(vlr));
+        if (right > GAIN_FLOAT_UNITY) {
+            right = GAIN_FLOAT_UNITY;
+        }
+        right *= v;
     }
 
     if (lastTrack) {
@@ -4157,7 +4164,10 @@
         mMixerStatus = MIXER_DRAIN_ALL;
         threadLoop_drain();
     }
-    mCallbackThread->exit();
+    if (mUseAsyncWrite) {
+        ALOG_ASSERT(mCallbackThread != 0);
+        mCallbackThread->exit();
+    }
     PlaybackThread::threadLoop_exit();
 }
 
@@ -5122,6 +5132,7 @@
         // to be at least 2 x the record thread frame count and cover audio hardware latency.
         // This is probably too conservative, but legacy application code may depend on it.
         // If you change this calculation, also review the start threshold which is related.
+        // FIXME It's not clear how input latency actually matters.  Perhaps this should be 0.
         uint32_t latencyMs = 50; // FIXME mInput->stream->get_latency(mInput->stream);
         size_t mNormalFrameCount = 2048; // FIXME
         uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
@@ -5354,12 +5365,12 @@
 
 void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args)
 {
-    fdprintf(fd, "\nInput thread %p:\n", this);
+    dprintf(fd, "\nInput thread %p:\n", this);
 
     if (mActiveTracks.size() > 0) {
-        fdprintf(fd, "  Buffer size: %zu bytes\n", mBufferSize);
+        dprintf(fd, "  Buffer size: %zu bytes\n", mBufferSize);
     } else {
-        fdprintf(fd, "  No active record clients\n");
+        dprintf(fd, "  No active record clients\n");
     }
 
     dumpBase(fd, args);
@@ -5374,9 +5385,9 @@
     size_t numtracks = mTracks.size();
     size_t numactive = mActiveTracks.size();
     size_t numactiveseen = 0;
-    fdprintf(fd, "  %d Tracks", numtracks);
+    dprintf(fd, "  %d Tracks", numtracks);
     if (numtracks) {
-        fdprintf(fd, " of which %d are active\n", numactive);
+        dprintf(fd, " of which %d are active\n", numactive);
         RecordTrack::appendDumpHeader(result);
         for (size_t i = 0; i < numtracks ; ++i) {
             sp<RecordTrack> track = mTracks[i];
@@ -5390,7 +5401,7 @@
             }
         }
     } else {
-        fdprintf(fd, "\n");
+        dprintf(fd, "\n");
     }
 
     if (numactiveseen != numactive) {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 1b01512..44008e5 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -301,6 +301,8 @@
                 // If a thread does not have such a heap, this method returns 0.
                 virtual sp<MemoryDealer>    readOnlyHeap() const { return 0; }
 
+                virtual sp<IMemory> pipeMemory() const { return 0; }
+
     mutable     Mutex                   mLock;
 
 protected:
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 5f13be3..4cba3fd 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -39,6 +39,13 @@
         STARTING_2,     // for RecordTrack only
     };
 
+    // where to allocate the data buffer
+    enum alloc_type {
+        ALLOC_CBLK,     // allocate immediately after control block
+        ALLOC_READONLY, // allocate from a separate read-only heap per thread
+        ALLOC_PIPE,     // do not allocate; use the pipe buffer
+    };
+
                         TrackBase(ThreadBase *thread,
                                 const sp<Client>& client,
                                 uint32_t sampleRate,
@@ -50,7 +57,7 @@
                                 int uid,
                                 IAudioFlinger::track_flags_t flags,
                                 bool isOut,
-                                bool useReadOnlyHeap = false);
+                                alloc_type alloc = ALLOC_CBLK);
     virtual             ~TrackBase();
     virtual status_t    initCheck() const { return getCblk() != 0 ? NO_ERROR : NO_MEMORY; }
 
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 222ec53..7ddc71c 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -21,6 +21,7 @@
 
 #include "Configuration.h"
 #include <math.h>
+#include <sys/syscall.h>
 #include <utils/Log.h>
 
 #include <private/media/AudioTrackShared.h>
@@ -34,6 +35,7 @@
 
 #include <media/nbaio/Pipe.h>
 #include <media/nbaio/PipeReader.h>
+#include <audio_utils/minifloat.h>
 
 // ----------------------------------------------------------------------------
 
@@ -71,7 +73,7 @@
             int clientUid,
             IAudioFlinger::track_flags_t flags,
             bool isOut,
-            bool useReadOnlyHeap)
+            alloc_type alloc)
     :   RefBase(),
         mThread(thread),
         mClient(client),
@@ -115,7 +117,7 @@
     // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
     size_t size = sizeof(audio_track_cblk_t);
     size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
-    if (sharedBuffer == 0 && !useReadOnlyHeap) {
+    if (sharedBuffer == 0 && alloc == ALLOC_CBLK) {
         size += bufferSize;
     }
 
@@ -137,7 +139,8 @@
     // construct the shared structure in-place.
     if (mCblk != NULL) {
         new(mCblk) audio_track_cblk_t();
-        if (useReadOnlyHeap) {
+        switch (alloc) {
+        case ALLOC_READONLY: {
             const sp<MemoryDealer> roHeap(thread->readOnlyHeap());
             if (roHeap == 0 ||
                     (mBufferMemory = roHeap->allocate(bufferSize)) == 0 ||
@@ -151,7 +154,17 @@
                 return;
             }
             memset(mBuffer, 0, bufferSize);
-        } else {
+            } break;
+        case ALLOC_PIPE:
+            mBufferMemory = thread->pipeMemory();
+            // mBuffer is the virtual address as seen from current process (mediaserver),
+            // and should normally be coming from mBufferMemory->pointer().
+            // However in this case the TrackBase does not reference the buffer directly.
+            // It should references the buffer via the pipe.
+            // Therefore, to detect incorrect usage of the buffer, we set mBuffer to NULL.
+            mBuffer = NULL;
+            break;
+        case ALLOC_CBLK:
             // clear all buffers
             if (sharedBuffer == 0) {
                 mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
@@ -162,6 +175,7 @@
                 mCblk->mFlags = CBLK_FORCEREADY;    // FIXME hack, need to fix the track ready logic
 #endif
             }
+            break;
         }
 
 #ifdef TEE_SINK
@@ -461,7 +475,7 @@
 
 void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size, bool active)
 {
-    uint32_t vlr = mAudioTrackServerProxy->getVolumeLR();
+    gain_minifloat_packed_t vlr = mAudioTrackServerProxy->getVolumeLR();
     if (isFastTrack()) {
         sprintf(buffer, "    F %2d", mFastIndex);
     } else if (mName >= AudioMixer::TRACK0) {
@@ -534,8 +548,8 @@
             stateChar,
             mFillingUpStatus,
             mAudioTrackServerProxy->getSampleRate(),
-            20.0 * log10((vlr & 0xFFFF) / 4096.0),
-            20.0 * log10((vlr >> 16) / 4096.0),
+            20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))),
+            20.0 * log10(float_from_gain(gain_minifloat_unpack_right(vlr))),
             mCblk->mServer,
             mMainBuffer,
             mAuxBuffer,
@@ -961,27 +975,27 @@
 
 // implement VolumeBufferProvider interface
 
-uint32_t AudioFlinger::PlaybackThread::Track::getVolumeLR()
+gain_minifloat_packed_t AudioFlinger::PlaybackThread::Track::getVolumeLR()
 {
     // called by FastMixer, so not allowed to take any locks, block, or do I/O including logs
     ALOG_ASSERT(isFastTrack() && (mCblk != NULL));
-    uint32_t vlr = mAudioTrackServerProxy->getVolumeLR();
-    uint32_t vl = vlr & 0xFFFF;
-    uint32_t vr = vlr >> 16;
+    gain_minifloat_packed_t vlr = mAudioTrackServerProxy->getVolumeLR();
+    float vl = float_from_gain(gain_minifloat_unpack_left(vlr));
+    float vr = float_from_gain(gain_minifloat_unpack_right(vlr));
     // track volumes come from shared memory, so can't be trusted and must be clamped
-    if (vl > MAX_GAIN_INT) {
-        vl = MAX_GAIN_INT;
+    if (vl > GAIN_FLOAT_UNITY) {
+        vl = GAIN_FLOAT_UNITY;
     }
-    if (vr > MAX_GAIN_INT) {
-        vr = MAX_GAIN_INT;
+    if (vr > GAIN_FLOAT_UNITY) {
+        vr = GAIN_FLOAT_UNITY;
     }
     // now apply the cached master volume and stream type volume;
     // this is trusted but lacks any synchronization or barrier so may be stale
     float v = mCachedVolume;
     vl *= v;
     vr *= v;
-    // re-combine into U4.16
-    vlr = (vr << 16) | (vl & 0xFFFF);
+    // re-combine into packed minifloat
+    vlr = gain_minifloat_pack(gain_from_float(vl), gain_from_float(vr));
     // FIXME look at mute, pause, and stop flags
     return vlr;
 }
@@ -1007,7 +1021,7 @@
     android_atomic_or(CBLK_INVALID, &cblk->mFlags);
     android_atomic_release_store(0x40000000, &cblk->mFutex);
     // client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE
-    (void) __futex_syscall3(&cblk->mFutex, FUTEX_WAKE, INT_MAX);
+    (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, INT_MAX);
     mIsInvalid = true;
 }
 
@@ -1592,7 +1606,7 @@
         // since client and server are in the same process,
         // the buffer has the same virtual address on both sides
         mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize);
-        mClientProxy->setVolumeLR((uint32_t(uint16_t(0x1000)) << 16) | uint16_t(0x1000));
+        mClientProxy->setVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY);
         mClientProxy->setSendLevel(0.0);
         mClientProxy->setSampleRate(sampleRate);
         mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize,
@@ -1840,7 +1854,7 @@
     :   TrackBase(thread, client, sampleRate, format,
                   channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid,
                   flags, false /*isOut*/,
-                  (flags & IAudioFlinger::TRACK_FAST) != 0 /*useReadOnlyHeap*/),
+                  (flags & IAudioFlinger::TRACK_FAST) != 0 ? ALLOC_READONLY : ALLOC_CBLK),
         mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
         // See real initialization of mRsmpInFront at RecordThread::start()
         mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)
@@ -1936,7 +1950,7 @@
     android_atomic_or(CBLK_INVALID, &cblk->mFlags);
     android_atomic_release_store(0x40000000, &cblk->mFutex);
     // client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE
-    (void) __futex_syscall3(&cblk->mFutex, FUTEX_WAKE, INT_MAX);
+    (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, INT_MAX);
 }
 
 
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index f270bfc..a22ad9d 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -5,7 +5,6 @@
 LOCAL_SRC_FILES:= \
     AudioPolicyService.cpp
 
-USE_LEGACY_AUDIO_POLICY = 1
 ifeq ($(USE_LEGACY_AUDIO_POLICY), 1)
 LOCAL_SRC_FILES += \
     AudioPolicyInterfaceImplLegacy.cpp \
@@ -15,8 +14,7 @@
 else
 LOCAL_SRC_FILES += \
     AudioPolicyInterfaceImpl.cpp \
-    AudioPolicyClientImpl.cpp \
-    AudioPolicyManager.cpp
+    AudioPolicyClientImpl.cpp
 endif
 
 LOCAL_C_INCLUDES := \
@@ -31,14 +29,42 @@
     libbinder \
     libmedia \
     libhardware \
-    libhardware_legacy
+    libhardware_legacy \
+
+ifneq ($(USE_LEGACY_AUDIO_POLICY), 1)
+LOCAL_SHARED_LIBRARIES += \
+    libaudiopolicymanager
+endif
 
 LOCAL_STATIC_LIBRARIES := \
     libmedia_helper \
     libserviceutility
 
-LOCAL_MODULE:= libaudiopolicy
+LOCAL_MODULE:= libaudiopolicyservice
 
 LOCAL_CFLAGS += -fvisibility=hidden
 
 include $(BUILD_SHARED_LIBRARY)
+
+ifneq ($(USE_LEGACY_AUDIO_POLICY), 1)
+ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    AudioPolicyManager.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libutils \
+    liblog
+
+LOCAL_STATIC_LIBRARIES := \
+    libmedia_helper
+
+LOCAL_MODULE:= libaudiopolicymanager
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
+endif
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index 62a44ee..bd9b15a 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -70,24 +70,36 @@
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_A2DP),
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_DIGITAL),
+    STRING_TO_ENUM(AUDIO_DEVICE_OUT_HDMI),
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET),
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET),
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_ACCESSORY),
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_DEVICE),
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_USB),
     STRING_TO_ENUM(AUDIO_DEVICE_OUT_REMOTE_SUBMIX),
+    STRING_TO_ENUM(AUDIO_DEVICE_OUT_TELEPHONY_TX),
+    STRING_TO_ENUM(AUDIO_DEVICE_OUT_LINE),
+    STRING_TO_ENUM(AUDIO_DEVICE_OUT_HDMI_ARC),
+    STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPDIF),
+    STRING_TO_ENUM(AUDIO_DEVICE_OUT_FM),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_BUILTIN_MIC),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_ALL_SCO),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_WIRED_HEADSET),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_AUX_DIGITAL),
+    STRING_TO_ENUM(AUDIO_DEVICE_IN_HDMI),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_VOICE_CALL),
+    STRING_TO_ENUM(AUDIO_DEVICE_IN_TELEPHONY_RX),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_BACK_MIC),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_REMOTE_SUBMIX),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_ACCESSORY),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_DEVICE),
+    STRING_TO_ENUM(AUDIO_DEVICE_IN_FM_TUNER),
+    STRING_TO_ENUM(AUDIO_DEVICE_IN_TV_TUNER),
+    STRING_TO_ENUM(AUDIO_DEVICE_IN_LINE),
+    STRING_TO_ENUM(AUDIO_DEVICE_IN_SPDIF),
 };
 
 const StringToEnum sFlagNameToEnumTable[] = {
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index fe1e707..9fd35e1 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -39,6 +39,8 @@
 #include <utils/String16.h>
 #include <utils/Trace.h>
 #include <system/camera_vendor_tags.h>
+#include <system/camera_metadata.h>
+#include <system/camera.h>
 
 #include "CameraService.h"
 #include "api1/CameraClient.h"
@@ -178,6 +180,9 @@
         {
            Mutex::Autolock al(mServiceLock);
 
+           /* Remove cached parameters from shim cache */
+           mShimParams.removeItem(cameraId);
+
            /* Find all clients that we need to disconnect */
            sp<BasicClient> client = mClient[cameraId].promote();
            if (client.get() != NULL) {
@@ -236,6 +241,96 @@
     return rc;
 }
 
+
+status_t CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) {
+    status_t ret = OK;
+    struct CameraInfo info;
+    if ((ret = getCameraInfo(cameraId, &info)) != OK) {
+        return ret;
+    }
+
+    CameraMetadata shimInfo;
+    int32_t orientation = static_cast<int32_t>(info.orientation);
+    if ((ret = shimInfo.update(ANDROID_SENSOR_ORIENTATION, &orientation, 1)) != OK) {
+        return ret;
+    }
+
+    uint8_t facing = (info.facing == CAMERA_FACING_FRONT) ?
+            ANDROID_LENS_FACING_FRONT : ANDROID_LENS_FACING_BACK;
+    if ((ret = shimInfo.update(ANDROID_LENS_FACING, &facing, 1)) != OK) {
+        return ret;
+    }
+
+    ssize_t index = -1;
+    {   // Scope for service lock
+        Mutex::Autolock lock(mServiceLock);
+        index = mShimParams.indexOfKey(cameraId);
+        // Release service lock so initializeShimMetadata can be called correctly.
+    }
+
+    if (index < 0) {
+        int64_t token = IPCThreadState::self()->clearCallingIdentity();
+        ret = initializeShimMetadata(cameraId);
+        IPCThreadState::self()->restoreCallingIdentity(token);
+        if (ret != OK) {
+            return ret;
+        }
+    }
+
+    Vector<Size> sizes;
+    Vector<int32_t> formats;
+    const char* supportedPreviewFormats;
+    {   // Scope for service lock
+        Mutex::Autolock lock(mServiceLock);
+        index = mShimParams.indexOfKey(cameraId);
+
+        mShimParams[index].getSupportedPreviewSizes(/*out*/sizes);
+
+        mShimParams[index].getSupportedPreviewFormats(/*out*/formats);
+    }
+
+    // Always include IMPLEMENTATION_DEFINED
+    formats.add(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
+
+    const size_t INTS_PER_CONFIG = 4;
+
+    // Build available stream configurations metadata
+    size_t streamConfigSize = sizes.size() * formats.size() * INTS_PER_CONFIG;
+    int32_t streamConfigs[streamConfigSize];
+    size_t configIndex = 0;
+    for (size_t i = 0; i < formats.size(); ++i) {
+        for (size_t j = 0; j < sizes.size(); ++j) {
+            streamConfigs[configIndex++] = formats[i];
+            streamConfigs[configIndex++] = sizes[j].width;
+            streamConfigs[configIndex++] = sizes[j].height;
+            streamConfigs[configIndex++] =
+                    ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT;
+        }
+    }
+
+    if ((ret = shimInfo.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+            streamConfigs, streamConfigSize)) != OK) {
+        return ret;
+    }
+
+    int64_t fakeMinFrames[0];
+    // TODO: Fixme, don't fake min frame durations.
+    if ((ret = shimInfo.update(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+            fakeMinFrames, 0)) != OK) {
+        return ret;
+    }
+
+    int64_t fakeStalls[0];
+    // TODO: Fixme, don't fake stall durations.
+    if ((ret = shimInfo.update(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
+            fakeStalls, 0)) != OK) {
+        return ret;
+    }
+
+    *cameraInfo = shimInfo;
+    return OK;
+}
+
 status_t CameraService::getCameraCharacteristics(int cameraId,
                                                 CameraMetadata* cameraInfo) {
     if (!cameraInfo) {
@@ -248,33 +343,37 @@
         return -ENODEV;
     }
 
-    if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_0) {
-        // TODO: Remove this check once HAL1 shim is in place.
-        ALOGE("%s: Only HAL module version V2 or higher supports static metadata", __FUNCTION__);
-        return BAD_VALUE;
-    }
-
     if (cameraId < 0 || cameraId >= mNumberOfCameras) {
         ALOGE("%s: Invalid camera id: %d", __FUNCTION__, cameraId);
         return BAD_VALUE;
     }
 
     int facing;
-    if (getDeviceVersion(cameraId, &facing) == CAMERA_DEVICE_API_VERSION_1_0) {
-        // TODO: Remove this check once HAL1 shim is in place.
-        ALOGE("%s: HAL1 doesn't support static metadata yet", __FUNCTION__);
-        return BAD_VALUE;
-    }
+    status_t ret = OK;
+    if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_0 ||
+            getDeviceVersion(cameraId, &facing) <= CAMERA_DEVICE_API_VERSION_2_1 ) {
+        /**
+         * Backwards compatibility mode for old HALs:
+         * - Convert CameraInfo into static CameraMetadata properties.
+         * - Retrieve cached CameraParameters for this camera.  If none exist,
+         *   attempt to open CameraClient and retrieve the CameraParameters.
+         * - Convert cached CameraParameters into static CameraMetadata
+         *   properties.
+         */
+        ALOGI("%s: Switching to HAL1 shim implementation...", __FUNCTION__);
 
-    if (getDeviceVersion(cameraId, &facing) <= CAMERA_DEVICE_API_VERSION_2_1) {
-        // Disable HAL2.x support for camera2 API for now.
-        ALOGW("%s: HAL2.x doesn't support getCameraCharacteristics for now", __FUNCTION__);
-        return BAD_VALUE;
-    }
+        if ((ret = generateShimMetadata(cameraId, cameraInfo)) != OK) {
+            return ret;
+        }
 
-    struct camera_info info;
-    status_t ret = mModule->get_camera_info(cameraId, &info);
-    *cameraInfo = info.static_camera_characteristics;
+    } else {
+        /**
+         * Normal HAL 2.1+ codepath.
+         */
+        struct camera_info info;
+        ret = mModule->get_camera_info(cameraId, &info);
+        *cameraInfo = info.static_camera_characteristics;
+    }
 
     return ret;
 }
@@ -285,12 +384,6 @@
         return -ENODEV;
     }
 
-    if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_2) {
-        // TODO: Remove this check once HAL1 shim is in place.
-        ALOGW("%s: Only HAL module version V2.2 or higher supports vendor tags", __FUNCTION__);
-        return -EOPNOTSUPP;
-    }
-
     desc = VendorTagDescriptor::getGlobalVendorTagDescriptor();
     return OK;
 }
@@ -372,6 +465,54 @@
     return true;
 }
 
+status_t CameraService::initializeShimMetadata(int cameraId) {
+    int pid = getCallingPid();
+    int uid = getCallingUid();
+    status_t ret = validateConnect(cameraId, uid);
+    if (ret != OK) {
+        return ret;
+    }
+
+    bool needsNewClient = false;
+    sp<Client> client;
+
+    String16 internalPackageName("media");
+    {   // Scope for service lock
+        Mutex::Autolock lock(mServiceLock);
+        if (mClient[cameraId] != NULL) {
+            client = static_cast<Client*>(mClient[cameraId].promote().get());
+        }
+        if (client == NULL) {
+            needsNewClient = true;
+            ret = connectHelperLocked(/*cameraClient*/NULL, // Empty binder callbacks
+                                      cameraId,
+                                      internalPackageName,
+                                      uid,
+                                      pid,
+                                      client);
+
+            if (ret != OK) {
+                return ret;
+            }
+        }
+
+        if (client == NULL) {
+            ALOGE("%s: Could not connect to client camera device.", __FUNCTION__);
+            return BAD_VALUE;
+        }
+
+        String8 rawParams = client->getParameters();
+        CameraParameters params(rawParams);
+        mShimParams.add(cameraId, params);
+    }
+
+    // Close client if one was opened solely for this call
+    if (needsNewClient) {
+        client->disconnect();
+    }
+    return OK;
+}
+
 status_t CameraService::validateConnect(int cameraId,
                                     /*inout*/
                                     int& clientUid) const {
@@ -468,6 +609,64 @@
     return true;
 }
 
+status_t CameraService::connectHelperLocked(const sp<ICameraClient>& cameraClient,
+                                      int cameraId,
+                                      const String16& clientPackageName,
+                                      int clientUid,
+                                      int callingPid,
+                                      /*out*/
+                                      sp<Client>& client) {
+
+    int facing = -1;
+    int deviceVersion = getDeviceVersion(cameraId, &facing);
+
+    // If there are other non-exclusive users of the camera,
+    //  this will tear them down before we can reuse the camera
+    if (isValidCameraId(cameraId)) {
+        // transition from PRESENT -> NOT_AVAILABLE
+        updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE,
+                     cameraId);
+    }
+
+    switch(deviceVersion) {
+      case CAMERA_DEVICE_API_VERSION_1_0:
+        client = new CameraClient(this, cameraClient,
+                clientPackageName, cameraId,
+                facing, callingPid, clientUid, getpid());
+        break;
+      case CAMERA_DEVICE_API_VERSION_2_0:
+      case CAMERA_DEVICE_API_VERSION_2_1:
+      case CAMERA_DEVICE_API_VERSION_3_0:
+      case CAMERA_DEVICE_API_VERSION_3_1:
+      case CAMERA_DEVICE_API_VERSION_3_2:
+        client = new Camera2Client(this, cameraClient,
+                clientPackageName, cameraId,
+                facing, callingPid, clientUid, getpid(),
+                deviceVersion);
+        break;
+      case -1:
+        ALOGE("Invalid camera id %d", cameraId);
+        return BAD_VALUE;
+      default:
+        ALOGE("Unknown camera device HAL version: %d", deviceVersion);
+        return INVALID_OPERATION;
+    }
+
+    status_t status = connectFinishUnsafe(client, client->getRemote());
+    if (status != OK) {
+        // this is probably not recoverable.. maybe the client can try again
+        // OK: we can only get here if we were originally in PRESENT state
+        updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId);
+        return status;
+    }
+
+    mClient[cameraId] = client;
+    LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId,
+         getpid());
+
+    return OK;
+}
+
 status_t CameraService::connect(
         const sp<ICameraClient>& cameraClient,
         int cameraId,
@@ -501,52 +700,16 @@
             return OK;
         }
 
-        int facing = -1;
-        int deviceVersion = getDeviceVersion(cameraId, &facing);
-
-        // If there are other non-exclusive users of the camera,
-        //  this will tear them down before we can reuse the camera
-        if (isValidCameraId(cameraId)) {
-            // transition from PRESENT -> NOT_AVAILABLE
-            updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE,
-                         cameraId);
-        }
-
-        switch(deviceVersion) {
-          case CAMERA_DEVICE_API_VERSION_1_0:
-            client = new CameraClient(this, cameraClient,
-                    clientPackageName, cameraId,
-                    facing, callingPid, clientUid, getpid());
-            break;
-          case CAMERA_DEVICE_API_VERSION_2_0:
-          case CAMERA_DEVICE_API_VERSION_2_1:
-          case CAMERA_DEVICE_API_VERSION_3_0:
-          case CAMERA_DEVICE_API_VERSION_3_1:
-          case CAMERA_DEVICE_API_VERSION_3_2:
-            client = new Camera2Client(this, cameraClient,
-                    clientPackageName, cameraId,
-                    facing, callingPid, clientUid, getpid(),
-                    deviceVersion);
-            break;
-          case -1:
-            ALOGE("Invalid camera id %d", cameraId);
-            return BAD_VALUE;
-          default:
-            ALOGE("Unknown camera device HAL version: %d", deviceVersion);
-            return INVALID_OPERATION;
-        }
-
-        status_t status = connectFinishUnsafe(client, client->getRemote());
+        status = connectHelperLocked(cameraClient,
+                                     cameraId,
+                                     clientPackageName,
+                                     clientUid,
+                                     callingPid,
+                                     client);
         if (status != OK) {
-            // this is probably not recoverable.. maybe the client can try again
-            // OK: we can only get here if we were originally in PRESENT state
-            updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId);
             return status;
         }
 
-        mClient[cameraId] = client;
-        LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId,
-             getpid());
     }
     // important: release the mutex here so the client can call back
     //    into the service from its destructor (can be at the end of the call)
@@ -561,8 +724,9 @@
     if (status != OK) {
         return status;
     }
-
-    remoteCallback->linkToDeath(this);
+    if (remoteCallback != NULL) {
+        remoteCallback->linkToDeath(this);
+    }
 
     return OK;
 }
@@ -800,9 +964,13 @@
     if (client != 0) {
         // Found our camera, clear and leave.
         LOG1("removeClient: clear camera %d", outIndex);
-        mClient[outIndex].clear();
 
-        client->getRemote()->unlinkToDeath(this);
+        sp<IBinder> remote = client->getRemote();
+        if (remote != NULL) {
+            remote->unlinkToDeath(this);
+        }
+
+        mClient[outIndex].clear();
     } else {
 
         sp<ProClient> clientPro = findProClientUnsafe(remoteBinder);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 76ea7be..ee39d52 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -18,6 +18,7 @@
 #define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
 
 #include <utils/Vector.h>
+#include <utils/KeyedVector.h>
 #include <binder/AppOpsManager.h>
 #include <binder/BinderService.h>
 #include <binder/IAppOpsCallback.h>
@@ -32,6 +33,7 @@
 #include <camera/camera2/ICameraDeviceCallbacks.h>
 #include <camera/VendorTagDescriptor.h>
 #include <camera/CaptureResult.h>
+#include <camera/CameraParameters.h>
 
 #include <camera/ICameraServiceListener.h>
 
@@ -395,6 +397,43 @@
     bool                isValidCameraId(int cameraId);
 
     bool                setUpVendorTags();
+
+    /**
+     * A mapping of camera ids to CameraParameters returned by that camera device.
+     *
+     * This cache is used to generate CameraCharacteristic metadata when using
+     * the HAL1 shim.
+     */
+    KeyedVector<int, CameraParameters>    mShimParams;
+
+    /**
+     * Initialize and cache the metadata used by the HAL1 shim for a given cameraId.
+     *
+     * Returns OK on success, or a negative error code.
+     */
+    status_t            initializeShimMetadata(int cameraId);
+
+    /**
+     * Generate the CameraCharacteristics metadata required by the Camera2 API
+     * from the available HAL1 CameraParameters and CameraInfo.
+     *
+     * Returns OK on success, or a negative error code.
+     */
+    status_t            generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo);
+
+    /**
+     * Connect a new camera client.  This should only be used while holding the
+     * mutex for mServiceLock.
+     *
+     * Returns OK on success, or a negative error code.
+     */
+    status_t            connectHelperLocked(const sp<ICameraClient>& cameraClient,
+                                      int cameraId,
+                                      const String16& clientPackageName,
+                                      int clientUid,
+                                      int callingPid,
+                                      /*out*/
+                                      sp<Client>& client);
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 65592d3..dece764 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -2028,24 +2028,7 @@
 }
 
 int Parameters::formatStringToEnum(const char *format) {
-    return
-        !format ?
-            HAL_PIXEL_FORMAT_YCrCb_420_SP :
-        !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422SP) ?
-            HAL_PIXEL_FORMAT_YCbCr_422_SP : // NV16
-        !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV420SP) ?
-            HAL_PIXEL_FORMAT_YCrCb_420_SP : // NV21
-        !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422I) ?
-            HAL_PIXEL_FORMAT_YCbCr_422_I :  // YUY2
-        !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV420P) ?
-            HAL_PIXEL_FORMAT_YV12 :         // YV12
-        !strcmp(format, CameraParameters::PIXEL_FORMAT_RGB565) ?
-            HAL_PIXEL_FORMAT_RGB_565 :      // RGB565
-        !strcmp(format, CameraParameters::PIXEL_FORMAT_RGBA8888) ?
-            HAL_PIXEL_FORMAT_RGBA_8888 :    // RGB8888
-        !strcmp(format, CameraParameters::PIXEL_FORMAT_BAYER_RGGB) ?
-            HAL_PIXEL_FORMAT_RAW_SENSOR :   // Raw sensor data
-        -1;
+    return CameraParameters::previewFormatToEnum(format);
 }
 
 const char* Parameters::formatEnumToString(int format) {
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 5a48a62..4fce1b3 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -246,6 +246,18 @@
     return res;
 }
 
+status_t CameraDeviceClient::beginConfigure() {
+    // TODO: Implement this.
+    ALOGE("%s: Not implemented yet.", __FUNCTION__);
+    return OK;
+}
+
+status_t CameraDeviceClient::endConfigure() {
+    // TODO: Implement this.
+    ALOGE("%s: Not implemented yet.", __FUNCTION__);
+    return OK;
+}
+
 status_t CameraDeviceClient::deleteStream(int streamId) {
     ATRACE_CALL();
     ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId);
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 0b37784..9981dfe 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -76,6 +76,10 @@
                                         /*out*/
                                         int64_t* lastFrameNumber = NULL);
 
+    virtual status_t beginConfigure();
+
+    virtual status_t endConfigure();
+
     // Returns -EBUSY if device is not idle
     virtual status_t      deleteStream(int streamId);
 
diff --git a/services/camera/libcameraservice/utils/CameraTraces.cpp b/services/camera/libcameraservice/utils/CameraTraces.cpp
index 346e15f..374dc5e 100644
--- a/services/camera/libcameraservice/utils/CameraTraces.cpp
+++ b/services/camera/libcameraservice/utils/CameraTraces.cpp
@@ -74,10 +74,10 @@
         return BAD_VALUE;
     }
 
-    fdprintf(fd, "Camera traces (%zu):\n", pcsList.size());
+    dprintf(fd, "Camera traces (%zu):\n", pcsList.size());
 
     if (pcsList.empty()) {
-        fdprintf(fd, "  No camera traces collected.\n");
+        dprintf(fd, "  No camera traces collected.\n");
     }
 
     // Print newest items first
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
index 0c7fbbd..41dab1f 100644
--- a/services/medialog/MediaLogService.cpp
+++ b/services/medialog/MediaLogService.cpp
@@ -60,7 +60,7 @@
     static const String16 sDump("android.permission.DUMP");
     if (!(IPCThreadState::self()->getCallingUid() == AID_MEDIA ||
             PermissionCache::checkCallingPermission(sDump))) {
-        fdprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n",
+        dprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n",
                 IPCThreadState::self()->getCallingPid(),
                 IPCThreadState::self()->getCallingUid());
         return NO_ERROR;
@@ -74,7 +74,7 @@
     for (size_t i = 0; i < namedReaders.size(); i++) {
         const NamedReader& namedReader = namedReaders[i];
         if (fd >= 0) {
-            fdprintf(fd, "\n%s:\n", namedReader.name());
+            dprintf(fd, "\n%s:\n", namedReader.name());
         } else {
             ALOGI("%s:", namedReader.name());
         }