Merge "MPEG4Extractor: guard against missing stbl."
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/cmds/screenrecord/Overlay.cpp b/cmds/screenrecord/Overlay.cpp
index 94f560d..c2a8f1b 100644
--- a/cmds/screenrecord/Overlay.cpp
+++ b/cmds/screenrecord/Overlay.cpp
@@ -47,7 +47,7 @@
         "ro.revision",
         "dalvik.vm.heapgrowthlimit",
         "dalvik.vm.heapsize",
-        "persist.sys.dalvik.vm.lib.1",
+        "persist.sys.dalvik.vm.lib.2",
         //"ro.product.cpu.abi",
         //"ro.bootloader",
         //"this-never-appears!",
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/AudioSystem.h b/include/media/AudioSystem.h
index 402b479..6fe0c7f 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -19,6 +19,7 @@
 
 #include <hardware/audio_effect.h>
 #include <media/IAudioFlingerClient.h>
+#include <media/IAudioPolicyServiceClient.h>
 #include <system/audio.h>
 #include <system/audio_policy.h>
 #include <utils/Errors.h>
@@ -274,8 +275,48 @@
     // check presence of audio flinger service.
     // returns NO_ERROR if binding to service succeeds, DEAD_OBJECT otherwise
     static status_t checkAudioFlinger();
+
+    /* List available audio ports and their attributes */
+    static status_t listAudioPorts(audio_port_role_t role,
+                                   audio_port_type_t type,
+                                   unsigned int *num_ports,
+                                   struct audio_port *ports,
+                                   unsigned int *generation);
+
+    /* Get attributes for a given audio port */
+    static status_t getAudioPort(struct audio_port *port);
+
+    /* Create an audio patch between several source and sink ports */
+    static status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle);
+
+    /* Release an audio patch */
+    static status_t releaseAudioPatch(audio_patch_handle_t handle);
+
+    /* List existing audio patches */
+    static status_t listAudioPatches(unsigned int *num_patches,
+                                      struct audio_patch *patches,
+                                      unsigned int *generation);
+    /* Set audio port configuration */
+    static status_t setAudioPortConfig(const struct audio_port_config *config);
+
     // ----------------------------------------------------------------------------
 
+    class AudioPortCallback : public RefBase
+    {
+    public:
+
+                AudioPortCallback() {}
+        virtual ~AudioPortCallback() {}
+
+        virtual void onAudioPortListUpdate() = 0;
+        virtual void onAudioPatchListUpdate() = 0;
+        virtual void onServiceDied() = 0;
+
+    };
+
+    static void setAudioPortCallback(sp<AudioPortCallback> callBack);
+
 private:
 
     class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
@@ -294,7 +335,8 @@
         virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
     };
 
-    class AudioPolicyServiceClient: public IBinder::DeathRecipient
+    class AudioPolicyServiceClient: public IBinder::DeathRecipient,
+                                    public BnAudioPolicyServiceClient
     {
     public:
         AudioPolicyServiceClient() {
@@ -302,6 +344,10 @@
 
         // DeathRecipient
         virtual void binderDied(const wp<IBinder>& who);
+
+        // IAudioPolicyServiceClient
+        virtual void onAudioPortListUpdate();
+        virtual void onAudioPatchListUpdate();
     };
 
     static sp<AudioFlingerClient> gAudioFlingerClient;
@@ -324,6 +370,8 @@
     // list of output descriptors containing cached parameters
     // (sampling rate, framecount, channel count...)
     static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
+
+    static sp<AudioPortCallback> gAudioPortCallback;
 };
 
 };  // namespace android
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 7db6a48..c742810 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -214,6 +214,27 @@
     // and should be called at most once.  For a definition of what "low RAM" means, see
     // android.app.ActivityManager.isLowRamDevice().
     virtual status_t setLowRamDevice(bool isLowRamDevice) = 0;
+
+    /* List available audio ports and their attributes */
+    virtual status_t listAudioPorts(unsigned int *num_ports,
+                                    struct audio_port *ports) = 0;
+
+    /* Get attributes for a given audio port */
+    virtual status_t getAudioPort(struct audio_port *port) = 0;
+
+    /* Create an audio patch between several source and sink ports */
+    virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle) = 0;
+
+    /* Release an audio patch */
+    virtual status_t releaseAudioPatch(audio_patch_handle_t handle) = 0;
+
+    /* List existing audio patches */
+    virtual status_t listAudioPatches(unsigned int *num_patches,
+                                      struct audio_patch *patches) = 0;
+    /* Set audio port configuration */
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+
 };
 
 
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index 09b9ea6..d422aa3 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -25,6 +25,7 @@
 #include <utils/Errors.h>
 #include <binder/IInterface.h>
 #include <media/AudioSystem.h>
+#include <media/IAudioPolicyServiceClient.h>
 
 #include <system/audio_policy.h>
 
@@ -99,6 +100,32 @@
    // Check if offload is possible for given format, stream type, sample rate,
     // bit rate, duration, video and streaming or offload property is enabled
     virtual bool isOffloadSupported(const audio_offload_info_t& info) = 0;
+
+    /* List available audio ports and their attributes */
+    virtual status_t listAudioPorts(audio_port_role_t role,
+                                    audio_port_type_t type,
+                                    unsigned int *num_ports,
+                                    struct audio_port *ports,
+                                    unsigned int *generation) = 0;
+
+    /* Get attributes for a given audio port */
+    virtual status_t getAudioPort(struct audio_port *port) = 0;
+
+    /* Create an audio patch between several source and sink ports */
+    virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle) = 0;
+
+    /* Release an audio patch */
+    virtual status_t releaseAudioPatch(audio_patch_handle_t handle) = 0;
+
+    /* List existing audio patches */
+    virtual status_t listAudioPatches(unsigned int *num_patches,
+                                      struct audio_patch *patches,
+                                      unsigned int *generation) = 0;
+    /* Set audio port configuration */
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+
+    virtual void registerClient(const sp<IAudioPolicyServiceClient>& client) = 0;
 };
 
 
diff --git a/include/media/IAudioPolicyServiceClient.h b/include/media/IAudioPolicyServiceClient.h
new file mode 100644
index 0000000..59df046
--- /dev/null
+++ b/include/media/IAudioPolicyServiceClient.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 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 ANDROID_IAUDIOPOLICYSERVICECLIENT_H
+#define ANDROID_IAUDIOPOLICYSERVICECLIENT_H
+
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <system/audio.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioPolicyServiceClient : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(AudioPolicyServiceClient);
+
+    // Notifies a change of audio port configuration.
+    virtual void onAudioPortListUpdate() = 0;
+    // Notifies a change of audio patch configuration.
+    virtual void onAudioPatchListUpdate() = 0;
+};
+
+
+// ----------------------------------------------------------------------------
+
+class BnAudioPolicyServiceClient : public BnInterface<IAudioPolicyServiceClient>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOPOLICYSERVICECLIENT_H
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 176f72d..dd13fea 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -38,8 +38,8 @@
 public:
     DECLARE_META_INTERFACE(OMX);
 
-    typedef void *buffer_id;
-    typedef void *node_id;
+    typedef uint32_t buffer_id;
+    typedef uint32_t node_id;
 
     // Given a node_id and the calling process' pid, returns true iff
     // the implementation of the OMX interface lives in the same
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 39e57de..a1e32c9 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -185,7 +185,7 @@
     };
 
     struct BufferInfo {
-        void *mBufferID;
+        uint32_t mBufferID;
         sp<ABuffer> mData;
         sp<ABuffer> mEncryptedData;
         sp<AMessage> mNotify;
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 28d121c..c07f4c9 100644
--- a/include/ndk/NdkMediaCodec.h
+++ b/include/ndk/NdkMediaCodec.h
@@ -30,6 +30,7 @@
 #include <android/native_window.h>
 
 #include "NdkMediaCrypto.h"
+#include "NdkMediaError.h"
 #include "NdkMediaFormat.h"
 
 #ifdef __cplusplus
@@ -78,12 +79,12 @@
 /**
  * delete the codec and free its resources
  */
-int AMediaCodec_delete(AMediaCodec*);
+media_status_t AMediaCodec_delete(AMediaCodec*);
 
 /**
  * Configure the codec. For decoding you would typically get the format from an extractor.
  */
-int AMediaCodec_configure(
+media_status_t AMediaCodec_configure(
         AMediaCodec*,
         const AMediaFormat* format,
         ANativeWindow* surface,
@@ -94,18 +95,18 @@
  * Start the codec. A codec must be configured before it can be started, and must be started
  * before buffers can be sent to it.
  */
-int AMediaCodec_start(AMediaCodec*);
+media_status_t AMediaCodec_start(AMediaCodec*);
 
 /**
  * Stop the codec.
  */
-int AMediaCodec_stop(AMediaCodec*);
+media_status_t AMediaCodec_stop(AMediaCodec*);
 
 /*
  * Flush the codec's input and output. All indices previously returned from calls to
  * AMediaCodec_dequeueInputBuffer and AMediaCodec_dequeueOutputBuffer become invalid.
  */
-int AMediaCodec_flush(AMediaCodec*);
+media_status_t AMediaCodec_flush(AMediaCodec*);
 
 /**
  * Get an input buffer. The specified buffer index must have been previously obtained from
@@ -129,13 +130,13 @@
 /**
  * Send the specified buffer to the codec for processing.
  */
-int AMediaCodec_queueInputBuffer(AMediaCodec*,
+media_status_t AMediaCodec_queueInputBuffer(AMediaCodec*,
         size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags);
 
 /**
  * Send the specified buffer to the codec for processing.
  */
-int AMediaCodec_queueSecureInputBuffer(AMediaCodec*,
+media_status_t AMediaCodec_queueSecureInputBuffer(AMediaCodec*,
         size_t idx, off_t offset, AMediaCodecCryptoInfo*, uint64_t time, uint32_t flags);
 
 /**
@@ -145,36 +146,48 @@
 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.
  */
-int AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render);
-
-
-typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
+media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render);
 
 /**
- * Set a callback to be called when a new buffer is available, or there was a format
- * or buffer change.
- * Note that you cannot perform any operations on the mediacodec from within the callback.
- * If you need to perform mediacodec operations, you must do so on a different thread.
+ * 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.
  */
-int AMediaCodec_setNotificationCallback(AMediaCodec*, OnCodecEvent callback, void *userdata);
+media_status_t AMediaCodec_releaseOutputBufferAtTime(
+        AMediaCodec *mData, size_t idx, int64_t timestampNs);
 
 
-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);
 
@@ -182,14 +195,38 @@
  * delete an AMediaCodecCryptoInfo created previously with AMediaCodecCryptoInfo_new, or
  * obtained from AMediaExtractor
  */
-int AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo*);
+media_status_t AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo*);
 
+/**
+ * The number of subsamples that make up the buffer's contents.
+ */
 size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo*);
-int AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo*, uint8_t *dst);
-int AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo*, uint8_t *dst);
-uint32_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo*);
-int AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo*, size_t *dst);
-int AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo*, size_t *dst);
+
+/**
+ * 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);
+
+/**
+ * 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
 } // extern "C"
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 1322a9d..10afdd9 100644
--- a/include/ndk/NdkMediaDrm.h
+++ b/include/ndk/NdkMediaDrm.h
@@ -27,6 +27,8 @@
 #ifndef _NDK_MEDIA_DRM_H
 #define _NDK_MEDIA_DRM_H
 
+#include "NdkMediaError.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -47,23 +49,6 @@
 typedef AMediaDrmByteArray AMediaDrmKeySetId;
 typedef AMediaDrmByteArray AMediaDrmSecureStop;
 
-#define MEDIADRM_ERROR_BASE -2000
-
-typedef enum {
-    MEDIADRM_OK = 0,
-    MEDIADRM_NOT_PROVISIONED_ERROR    = MEDIADRM_ERROR_BASE - 1,
-    MEDIADRM_RESOURCE_BUSY_ERROR      = MEDIADRM_ERROR_BASE - 2,
-    MEDIADRM_DEVICE_REVOKED_ERROR     = MEDIADRM_ERROR_BASE - 3,
-    MEDIADRM_SHORT_BUFFER             = MEDIADRM_ERROR_BASE - 4,
-    MEDIADRM_INVALID_OBJECT_ERROR     = MEDIADRM_ERROR_BASE - 5,
-    MEDIADRM_INVALID_PARAMETER_ERROR  = MEDIADRM_ERROR_BASE - 6,
-    MEDIADRM_SESSION_NOT_OPENED_ERROR = MEDIADRM_ERROR_BASE - 7,
-    MEDIADRM_TAMPER_DETECTED_ERROR    = MEDIADRM_ERROR_BASE - 8,
-    MEDIADRM_VERIFY_FAILED            = MEDIADRM_ERROR_BASE - 9,
-    MEDIADRM_NEED_KEY_ERROR           = MEDIADRM_ERROR_BASE - 10,
-    MEDIADRM_LICENSE_EXPIRED_ERROR    = MEDIADRM_ERROR_BASE - 11,
-    MEDIADRM_UNKNOWN_ERROR            = MEDIADRM_ERROR_BASE - 12,
-} mediadrm_status_t;
 
 typedef enum AMediaDrmEventType {
     /**
@@ -92,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);
 
 
@@ -122,7 +107,7 @@
  *
  * listener is the callback that will be invoked on event
  */
-void AMediaDrm_setOnEventListener(AMediaDrm *, AMediaDrmEventListener listener);
+media_status_t AMediaDrm_setOnEventListener(AMediaDrm *, AMediaDrmEventListener listener);
 
 /**
  * Open a new session with the MediaDrm object.  A session ID is returned.
@@ -130,13 +115,13 @@
  * returns MEDIADRM_NOT_PROVISIONED_ERROR if provisioning is needed
  * returns MEDIADRM_RESOURCE_BUSY_ERROR if required resources are in use
  */
-mediadrm_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.
  */
-mediadrm_status_t AMediaDrm_closeSession(AMediaDrm *, const AMediaDrmSessionId &sessionId);
+media_status_t AMediaDrm_closeSession(AMediaDrm *, const AMediaDrmSessionId *sessionId);
 
 typedef enum AMediaDrmKeyType {
     /**
@@ -213,10 +198,10 @@
  * returns MEDIADRM_NOT_PROVISIONED_ERROR if reprovisioning is needed, due to a
  * problem with the device certificate.
 */
-mediadrm_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
@@ -235,8 +220,8 @@
  * responseSize should be set to the size of the response in bytes
  */
 
-mediadrm_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
@@ -245,15 +230,15 @@
  * sessionId is the session ID for the DRM session
  * keySetId identifies the saved key set to restore
  */
-mediadrm_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
  */
-mediadrm_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
@@ -268,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.
  */
-mediadrm_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);
 
 
 /**
@@ -287,8 +272,8 @@
  *       the provisioning request should be sent to.  It will remain accessible until
  *       the next call to getProvisionRequest.
  */
-mediadrm_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);
 
 
 /**
@@ -302,7 +287,7 @@
  * returns MEDIADRM_DEVICE_REVOKED_ERROR if the response indicates that the
  * server rejected the request
  */
-mediadrm_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *,
+media_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *,
         const uint8_t *response, size_t responseSize);
 
 
@@ -327,8 +312,8 @@
  * MEDIADRM_SHORT_BUFFER will be returned and *numSecureStops will be set to the
  * number required.
  */
-mediadrm_status_t AMediaDrm_getSecureStops(AMediaDrm *,
-        AMediaDrmSecureStop *secureStops, size_t &numSecureStops);
+media_status_t AMediaDrm_getSecureStops(AMediaDrm *,
+        AMediaDrmSecureStop *secureStops, size_t *numSecureStops);
 
 /**
  * Process the SecureStop server response message ssRelease.  After authenticating
@@ -336,8 +321,8 @@
  *
  * ssRelease is the server response indicating which secure stops to release
  */
-mediadrm_status_t AMediaDrm_releaseSecureStops(AMediaDrm *,
-        const AMediaDrmSecureStop &ssRelease);
+media_status_t AMediaDrm_releaseSecureStops(AMediaDrm *,
+        const AMediaDrmSecureStop *ssRelease);
 
 /**
  * String property name: identifies the maker of the DRM engine plugin
@@ -369,8 +354,8 @@
  * memory that the value resides in is owned by the NDK MediaDrm API and
  * will remain valid until the next call to AMediaDrm_getPropertyString.
  */
-mediadrm_status_t AMediaDrm_getPropertyString(AMediaDrm *, const char *propertyName,
-        const char *&propertyValue);
+media_status_t AMediaDrm_getPropertyString(AMediaDrm *, const char *propertyName,
+        const char **propertyValue);
 
 /**
  * Byte array property name: the device unique identifier is established during
@@ -384,19 +369,19 @@
  * memory that the value resides in is owned by the NDK MediaDrm API and
  * will remain valid until the next call to AMediaDrm_getPropertyByteArray.
  */
-mediadrm_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *, const char *propertyName,
-        AMediaDrmByteArray &propertyValue);
+media_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *, const char *propertyName,
+        AMediaDrmByteArray *propertyValue);
 
 /**
  * Set a DRM engine plugin String property value.
  */
-mediadrm_status_t AMediaDrm_setPropertyString(AMediaDrm *, const char *propertyName,
+media_status_t AMediaDrm_setPropertyString(AMediaDrm *, const char *propertyName,
         const char *value);
 
 /**
  * Set a DRM engine plugin byte array property value.
  */
-mediadrm_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *, const char *propertyName,
+media_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *, const char *propertyName,
         const uint8_t *value, size_t valueSize);
 
 /**
@@ -424,7 +409,7 @@
  * to use is identified by the 16 byte keyId.  The key must have been loaded into
  * the session using provideKeyResponse.
  */
-mediadrm_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);
 
@@ -435,7 +420,7 @@
  * to use is identified by the 16 byte keyId.  The key must have been loaded into
  * the session using provideKeyResponse.
  */
-mediadrm_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);
 
@@ -448,7 +433,7 @@
  * by the 16 byte keyId.  The key must have been loaded into the session using
  * provideKeyResponse.
  */
-mediadrm_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);
 
@@ -459,7 +444,7 @@
  * use is identified by the 16 byte keyId.  The key must have been loaded into the
  * session using provideKeyResponse.
  */
-mediadrm_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/NdkMediaError.h b/include/ndk/NdkMediaError.h
index b89a10e..12613eb 100644
--- a/include/ndk/NdkMediaError.h
+++ b/include/ndk/NdkMediaError.h
@@ -32,13 +32,28 @@
 extern "C" {
 #endif
 
-enum {
-    AMEDIAERROR_BASE = -10000,
+typedef enum {
+    AMEDIA_OK = 0,
 
-    AMEDIAERROR_GENERIC     = AMEDIAERROR_BASE,
-    AMEDIAERROR_MALFORMED   = AMEDIAERROR_BASE - 1,
-    AMEDIAERROR_UNSUPPORTED = AMEDIAERROR_BASE - 2
-};
+    AMEDIA_ERROR_BASE                  = -10000,
+    AMEDIA_ERROR_UNKNOWN               = AMEDIA_ERROR_BASE,
+    AMEDIA_ERROR_MALFORMED             = AMEDIA_ERROR_BASE - 1,
+    AMEDIA_ERROR_UNSUPPORTED           = AMEDIA_ERROR_BASE - 2,
+    AMEDIA_ERROR_INVALID_OBJECT        = AMEDIA_ERROR_BASE - 3,
+    AMEDIA_ERROR_INVALID_PARAMETER     = AMEDIA_ERROR_BASE - 4,
+
+    AMEDIA_DRM_ERROR_BASE              = -20000,
+    AMEDIA_DRM_NOT_PROVISIONED         = AMEDIA_DRM_ERROR_BASE - 1,
+    AMEDIA_DRM_RESOURCE_BUSY           = AMEDIA_DRM_ERROR_BASE - 2,
+    AMEDIA_DRM_DEVICE_REVOKED          = AMEDIA_DRM_ERROR_BASE - 3,
+    AMEDIA_DRM_SHORT_BUFFER            = AMEDIA_DRM_ERROR_BASE - 4,
+    AMEDIA_DRM_SESSION_NOT_OPENED      = AMEDIA_DRM_ERROR_BASE - 5,
+    AMEDIA_DRM_TAMPER_DETECTED         = AMEDIA_DRM_ERROR_BASE - 6,
+    AMEDIA_DRM_VERIFY_FAILED           = AMEDIA_DRM_ERROR_BASE - 7,
+    AMEDIA_DRM_NEED_KEY                = AMEDIA_DRM_ERROR_BASE - 8,
+    AMEDIA_DRM_LICENSE_EXPIRED         = AMEDIA_DRM_ERROR_BASE - 9,
+
+} media_status_t;
 
 
 #ifdef __cplusplus
diff --git a/include/ndk/NdkMediaExtractor.h b/include/ndk/NdkMediaExtractor.h
index 9e50ec0..7a4e702 100644
--- a/include/ndk/NdkMediaExtractor.h
+++ b/include/ndk/NdkMediaExtractor.h
@@ -50,22 +50,22 @@
 /**
  * Delete a previously created media extractor
  */
-int AMediaExtractor_delete(AMediaExtractor*);
+media_status_t AMediaExtractor_delete(AMediaExtractor*);
 
 /**
  *  Set the file descriptor from which the extractor will read.
  */
-int AMediaExtractor_setDataSourceFd(AMediaExtractor*, int fd, off64_t offset, off64_t length);
+media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor*, int fd, off64_t offset, off64_t length);
 
 /**
  * Set the URI from which the extractor will read.
  */
-int AMediaExtractor_setDataSource(AMediaExtractor*, const char *location); // TODO support headers
+media_status_t AMediaExtractor_setDataSource(AMediaExtractor*, const char *location); // TODO support headers
 
 /**
  * Return the number of tracks in the previously specified media file
  */
-int AMediaExtractor_getTrackCount(AMediaExtractor*);
+size_t AMediaExtractor_getTrackCount(AMediaExtractor*);
 
 /**
  * Return the format of the specified track. The caller must free the returned format
@@ -78,23 +78,23 @@
  * Selecting the same track multiple times has no effect, the track is
  * only selected once.
  */
-int AMediaExtractor_selectTrack(AMediaExtractor*, size_t idx);
+media_status_t AMediaExtractor_selectTrack(AMediaExtractor*, size_t idx);
 
 /**
  * Unselect the specified track. Subsequent calls to readSampleData, getSampleTrackIndex and
  * getSampleTime only retrieve information for the subset of tracks selected..
  */
-int AMediaExtractor_unselectTrack(AMediaExtractor*, size_t idx);
+media_status_t AMediaExtractor_unselectTrack(AMediaExtractor*, size_t idx);
 
 /**
  * Read the current sample.
  */
-int AMediaExtractor_readSampleData(AMediaExtractor*, uint8_t *buffer, size_t capacity);
+ssize_t AMediaExtractor_readSampleData(AMediaExtractor*, uint8_t *buffer, size_t capacity);
 
 /**
  * Read the current sample's flags.
  */
-int AMediaExtractor_getSampleFlags(AMediaExtractor*); // see definitions below
+uint32_t AMediaExtractor_getSampleFlags(AMediaExtractor*); // see definitions below
 
 /**
  * Returns the track index the current sample originates from (or -1
@@ -106,7 +106,7 @@
  * Returns the current sample's presentation time in microseconds.
  * or -1 if no more samples are available.
  */
-int64_t AMediaExtractor_getSampletime(AMediaExtractor*);
+int64_t AMediaExtractor_getSampleTime(AMediaExtractor*);
 
 /**
  * Advance to the next sample. Returns false if no more sample data
@@ -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/NdkMediaFormat.h b/include/ndk/NdkMediaFormat.h
index e0caeab..ab29791 100644
--- a/include/ndk/NdkMediaFormat.h
+++ b/include/ndk/NdkMediaFormat.h
@@ -29,6 +29,8 @@
 
 #include <sys/types.h>
 
+#include "NdkMediaError.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -37,7 +39,7 @@
 typedef struct AMediaFormat AMediaFormat;
 
 AMediaFormat *AMediaFormat_new();
-int AMediaFormat_delete(AMediaFormat*);
+media_status_t AMediaFormat_delete(AMediaFormat*);
 
 /**
  * Human readable representation of the format. The returned string is owned by the format,
diff --git a/include/ndk/NdkMediaMuxer.h b/include/ndk/NdkMediaMuxer.h
index deb150d..90d946c 100644
--- a/include/ndk/NdkMediaMuxer.h
+++ b/include/ndk/NdkMediaMuxer.h
@@ -30,8 +30,9 @@
 
 #include <sys/types.h>
 
-#include "NdkMediaFormat.h"
 #include "NdkMediaCodec.h"
+#include "NdkMediaError.h"
+#include "NdkMediaFormat.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -53,20 +54,63 @@
 /**
  * Delete a previously created media muxer
  */
-int AMediaMuxer_delete(AMediaMuxer*);
+media_status_t AMediaMuxer_delete(AMediaMuxer*);
 
-int 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);
 
-int AMediaMuxer_setOrientationHint(AMediaMuxer*, int degrees);
+/**
+ * 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);
 
-int AMediaMuxer_start(AMediaMuxer*);
+/**
+ * Start the muxer. Should be called after AMediaMuxer_addTrack and
+ * before AMediaMuxer_writeSampleData.
+ */
+media_status_t AMediaMuxer_start(AMediaMuxer*);
 
-int AMediaMuxer_stop(AMediaMuxer*);
+/**
+ * Stops the muxer.
+ * Once the muxer stops, it can not be restarted.
+ */
+media_status_t AMediaMuxer_stop(AMediaMuxer*);
 
-int AMediaMuxer_writeSampleData(AMediaMuxer *muxer,
-        size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo &info);
+/**
+ * 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);
 
 #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/libcpustats/Android.mk b/media/libcpustats/Android.mk
index b506353..ee283a6 100644
--- a/media/libcpustats/Android.mk
+++ b/media/libcpustats/Android.mk
@@ -1,4 +1,4 @@
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
 
@@ -8,4 +8,6 @@
 
 LOCAL_MODULE := libcpustats
 
+LOCAL_CFLAGS := -std=gnu++11 -Werror
+
 include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libcpustats/ThreadCpuUsage.cpp b/media/libcpustats/ThreadCpuUsage.cpp
index 637402a..cfdcb51 100644
--- a/media/libcpustats/ThreadCpuUsage.cpp
+++ b/media/libcpustats/ThreadCpuUsage.cpp
@@ -21,7 +21,6 @@
 #include <stdlib.h>
 #include <time.h>
 
-#include <utils/Debug.h>
 #include <utils/Log.h>
 
 #include <cpustats/ThreadCpuUsage.h>
@@ -218,7 +217,7 @@
 #define FREQ_SIZE 64
             char freq_path[FREQ_SIZE];
 #define FREQ_DIGIT 27
-            COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10);
+            static_assert(MAX_CPU <= 10, "MAX_CPU too large");
 #define FREQ_PATH "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq"
             strlcpy(freq_path, FREQ_PATH, sizeof(freq_path));
             freq_path[FREQ_DIGIT] = cpuNum + '0';
diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c
index 661fde9..6686f27 100644
--- a/media/libeffects/downmix/EffectDownmix.c
+++ b/media/libeffects/downmix/EffectDownmix.c
@@ -118,7 +118,7 @@
         hasBacks = true;
     }
 
-    const int numChan = popcount(mask);
+    const int numChan = audio_channel_count_from_out_mask(mask);
     const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
     const bool hasLFE =
             ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
@@ -629,7 +629,8 @@
             ALOGE("Downmix_Configure error: input channel mask can't be 0");
             return -EINVAL;
         }
-        pDownmixer->input_channel_count = popcount(pConfig->inputCfg.channels);
+        pDownmixer->input_channel_count =
+                audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
     }
 
     Downmix_Reset(pDownmixer, init);
@@ -997,7 +998,7 @@
         hasBacks = true;
     }
 
-    const int numChan = popcount(mask);
+    const int numChan = audio_channel_count_from_out_mask(mask);
     const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
     const bool hasLFE =
             ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index a96a703..cf98f56 100644
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -879,8 +879,8 @@
 int Session_SetConfig(preproc_session_t *session, effect_config_t *config)
 {
     uint32_t sr;
-    uint32_t inCnl = popcount(config->inputCfg.channels);
-    uint32_t outCnl = popcount(config->outputCfg.channels);
+    uint32_t inCnl = audio_channel_count_from_out_mask(config->inputCfg.channels);
+    uint32_t outCnl = audio_channel_count_from_out_mask(config->outputCfg.channels);
 
     if (config->inputCfg.samplingRate != config->outputCfg.samplingRate ||
         config->inputCfg.format != config->outputCfg.format ||
@@ -1035,7 +1035,7 @@
             config->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
         return -EINVAL;
     }
-    uint32_t inCnl = popcount(config->inputCfg.channels);
+    uint32_t inCnl = audio_channel_count_from_out_mask(config->inputCfg.channels);
     int status = session->apm->set_num_reverse_channels(inCnl);
     if (status < 0) {
         return -EINVAL;
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 47cab62..e5089da 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -207,7 +207,8 @@
     pContext->mScalingMode = VISUALIZER_SCALING_MODE_NORMALIZED;
 
     // measurement initialization
-    pContext->mChannelCount = popcount(pContext->mConfig.inputCfg.channels);
+    pContext->mChannelCount =
+            audio_channel_count_from_out_mask(pContext->mConfig.inputCfg.channels);
     pContext->mMeasurementMode = MEASUREMENT_MODE_NONE;
     pContext->mMeasurementWindowSizeInBuffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS;
     pContext->mMeasurementBufferIdx = 0;
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index f3770e4..69eead3 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -44,6 +44,7 @@
     JetPlayer.cpp \
     IOMX.cpp \
     IAudioPolicyService.cpp \
+    IAudioPolicyServiceClient.cpp \
     MediaScanner.cpp \
     MediaScannerClient.cpp \
     CharacterEncodingDetector.cpp \
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 97ab8f8..db61e85 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -51,7 +51,8 @@
 
     // We double the size of input buffer for ping pong use of record buffer.
     // Assumes audio_is_linear_pcm(format)
-    if ((*frameCount = (size * 2) / (popcount(channelMask) * audio_bytes_per_sample(format))) == 0) {
+    if ((*frameCount = (size * 2) / (audio_channel_count_from_in_mask(channelMask) *
+            audio_bytes_per_sample(format))) == 0) {
         ALOGE("Unsupported configuration: sampleRate %u, format %#x, channelMask %#x",
             sampleRate, format, channelMask);
         return BAD_VALUE;
@@ -193,7 +194,7 @@
         return BAD_VALUE;
     }
     mChannelMask = channelMask;
-    uint32_t channelCount = popcount(channelMask);
+    uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
     mChannelCount = channelCount;
 
     if (audio_is_linear_pcm(format)) {
@@ -202,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;
 
@@ -241,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) {
@@ -463,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/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 2f16444..eafb3ad 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -45,6 +45,7 @@
 audio_channel_mask_t AudioSystem::gPrevInChannelMask;
 size_t AudioSystem::gInBuffSize = 0;    // zero indicates cache is invalid
 
+sp<AudioSystem::AudioPortCallback> AudioSystem::gAudioPortCallback;
 
 // establish binder interface to AudioFlinger service
 const sp<IAudioFlinger>& AudioSystem::get_audio_flinger()
@@ -528,6 +529,7 @@
     gAudioErrorCallback = cb;
 }
 
+
 bool AudioSystem::routedToA2dpOutput(audio_stream_type_t streamType)
 {
     switch (streamType) {
@@ -566,6 +568,7 @@
         }
         binder->linkToDeath(gAudioPolicyServiceClient);
         gAudioPolicyService = interface_cast<IAudioPolicyService>(binder);
+        gAudioPolicyService->registerClient(gAudioPolicyServiceClient);
         gLock.unlock();
     } else {
         gLock.unlock();
@@ -831,14 +834,88 @@
     return aps->isOffloadSupported(info);
 }
 
+status_t AudioSystem::listAudioPorts(audio_port_role_t role,
+                                     audio_port_type_t type,
+                                     unsigned int *num_ports,
+                                     struct audio_port *ports,
+                                     unsigned int *generation)
+{
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    return aps->listAudioPorts(role, type, num_ports, ports, generation);
+}
+
+status_t AudioSystem::getAudioPort(struct audio_port *port)
+{
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    return aps->getAudioPort(port);
+}
+
+status_t AudioSystem::createAudioPatch(const struct audio_patch *patch,
+                                   audio_patch_handle_t *handle)
+{
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    return aps->createAudioPatch(patch, handle);
+}
+
+status_t AudioSystem::releaseAudioPatch(audio_patch_handle_t handle)
+{
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    return aps->releaseAudioPatch(handle);
+}
+
+status_t AudioSystem::listAudioPatches(unsigned int *num_patches,
+                                  struct audio_patch *patches,
+                                  unsigned int *generation)
+{
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    return aps->listAudioPatches(num_patches, patches, generation);
+}
+
+status_t AudioSystem::setAudioPortConfig(const struct audio_port_config *config)
+{
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    return aps->setAudioPortConfig(config);
+}
+
+void AudioSystem::setAudioPortCallback(sp<AudioPortCallback> callBack)
+{
+    Mutex::Autolock _l(gLock);
+    gAudioPortCallback = callBack;
+}
+
 // ---------------------------------------------------------------------------
 
 void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused)
 {
-    Mutex::Autolock _l(AudioSystem::gLock);
+    Mutex::Autolock _l(gLock);
+    if (gAudioPortCallback != 0) {
+        gAudioPortCallback->onServiceDied();
+    }
     AudioSystem::gAudioPolicyService.clear();
 
     ALOGW("AudioPolicyService server died!");
 }
 
+void AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate()
+{
+    Mutex::Autolock _l(gLock);
+    if (gAudioPortCallback != 0) {
+        gAudioPortCallback->onAudioPortListUpdate();
+    }
+}
+
+void AudioSystem::AudioPolicyServiceClient::onAudioPatchListUpdate()
+{
+    Mutex::Autolock _l(gLock);
+    if (gAudioPortCallback != 0) {
+        gAudioPortCallback->onAudioPatchListUpdate();
+    }
+}
+
 }; // namespace android
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index aaaa3f1..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>
@@ -290,7 +291,7 @@
         return BAD_VALUE;
     }
     mChannelMask = channelMask;
-    uint32_t channelCount = popcount(channelMask);
+    uint32_t channelCount = audio_channel_count_from_out_mask(channelMask);
     mChannelCount = channelCount;
 
     // AudioFlinger does not currently support 8-bit data in shared memory
@@ -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/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 0e2463e..687fa76 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -74,6 +74,12 @@
     GET_PRIMARY_OUTPUT_SAMPLING_RATE,
     GET_PRIMARY_OUTPUT_FRAME_COUNT,
     SET_LOW_RAM_DEVICE,
+    LIST_AUDIO_PORTS,
+    GET_AUDIO_PORT,
+    CREATE_AUDIO_PATCH,
+    RELEASE_AUDIO_PATCH,
+    LIST_AUDIO_PATCHES,
+    SET_AUDIO_PORT_CONFIG
 };
 
 class BpAudioFlinger : public BpInterface<IAudioFlinger>
@@ -801,7 +807,101 @@
         remote()->transact(SET_LOW_RAM_DEVICE, data, &reply);
         return reply.readInt32();
     }
-
+    virtual status_t listAudioPorts(unsigned int *num_ports,
+                                    struct audio_port *ports)
+    {
+        if (num_ports == NULL || *num_ports == 0 || ports == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.writeInt32(*num_ports);
+        status_t status = remote()->transact(LIST_AUDIO_PORTS, data, &reply);
+        if (status != NO_ERROR ||
+                (status = (status_t)reply.readInt32()) != NO_ERROR) {
+            return status;
+        }
+        *num_ports = (unsigned int)reply.readInt32();
+        reply.read(ports, *num_ports * sizeof(struct audio_port));
+        return status;
+    }
+    virtual status_t getAudioPort(struct audio_port *port)
+    {
+        if (port == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.write(port, sizeof(struct audio_port));
+        status_t status = remote()->transact(GET_AUDIO_PORT, data, &reply);
+        if (status != NO_ERROR ||
+                (status = (status_t)reply.readInt32()) != NO_ERROR) {
+            return status;
+        }
+        reply.read(port, sizeof(struct audio_port));
+        return status;
+    }
+    virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle)
+    {
+        if (patch == NULL || handle == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.write(patch, sizeof(struct audio_patch));
+        data.write(handle, sizeof(audio_patch_handle_t));
+        status_t status = remote()->transact(CREATE_AUDIO_PATCH, data, &reply);
+        if (status != NO_ERROR ||
+                (status = (status_t)reply.readInt32()) != NO_ERROR) {
+            return status;
+        }
+        reply.read(handle, sizeof(audio_patch_handle_t));
+        return status;
+    }
+    virtual status_t releaseAudioPatch(audio_patch_handle_t handle)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.write(&handle, sizeof(audio_patch_handle_t));
+        status_t status = remote()->transact(RELEASE_AUDIO_PATCH, data, &reply);
+        if (status != NO_ERROR) {
+            status = (status_t)reply.readInt32();
+        }
+        return status;
+    }
+    virtual status_t listAudioPatches(unsigned int *num_patches,
+                                      struct audio_patch *patches)
+    {
+        if (num_patches == NULL || *num_patches == 0 || patches == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.writeInt32(*num_patches);
+        status_t status = remote()->transact(LIST_AUDIO_PATCHES, data, &reply);
+        if (status != NO_ERROR ||
+                (status = (status_t)reply.readInt32()) != NO_ERROR) {
+            return status;
+        }
+        *num_patches = (unsigned int)reply.readInt32();
+        reply.read(patches, *num_patches * sizeof(struct audio_patch));
+        return status;
+    }
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config)
+    {
+        if (config == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+        data.write(config, sizeof(struct audio_port_config));
+        status_t status = remote()->transact(SET_AUDIO_PORT_CONFIG, data, &reply);
+        if (status != NO_ERROR) {
+            status = (status_t)reply.readInt32();
+        }
+        return status;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
@@ -1199,6 +1299,76 @@
             reply->writeInt32(setLowRamDevice(isLowRamDevice));
             return NO_ERROR;
         } break;
+        case LIST_AUDIO_PORTS: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            unsigned int num_ports = data.readInt32();
+            struct audio_port *ports =
+                    (struct audio_port *)calloc(num_ports,
+                                                           sizeof(struct audio_port));
+            status_t status = listAudioPorts(&num_ports, ports);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->writeInt32(num_ports);
+                reply->write(&ports, num_ports * sizeof(struct audio_port));
+            }
+            free(ports);
+            return NO_ERROR;
+        } break;
+        case GET_AUDIO_PORT: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            struct audio_port port;
+            data.read(&port, sizeof(struct audio_port));
+            status_t status = getAudioPort(&port);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->write(&port, sizeof(struct audio_port));
+            }
+            return NO_ERROR;
+        } break;
+        case CREATE_AUDIO_PATCH: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            struct audio_patch patch;
+            data.read(&patch, sizeof(struct audio_patch));
+            audio_patch_handle_t handle;
+            data.read(&handle, sizeof(audio_patch_handle_t));
+            status_t status = createAudioPatch(&patch, &handle);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->write(&handle, sizeof(audio_patch_handle_t));
+            }
+            return NO_ERROR;
+        } break;
+        case RELEASE_AUDIO_PATCH: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            audio_patch_handle_t handle;
+            data.read(&handle, sizeof(audio_patch_handle_t));
+            status_t status = releaseAudioPatch(handle);
+            reply->writeInt32(status);
+            return NO_ERROR;
+        } break;
+        case LIST_AUDIO_PATCHES: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            unsigned int num_patches = data.readInt32();
+            struct audio_patch *patches =
+                    (struct audio_patch *)calloc(num_patches,
+                                                 sizeof(struct audio_patch));
+            status_t status = listAudioPatches(&num_patches, patches);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->writeInt32(num_patches);
+                reply->write(&patches, num_patches * sizeof(struct audio_patch));
+            }
+            free(patches);
+            return NO_ERROR;
+        } break;
+        case SET_AUDIO_PORT_CONFIG: {
+            CHECK_INTERFACE(IAudioFlinger, data, reply);
+            struct audio_port_config config;
+            data.read(&config, sizeof(struct audio_port_config));
+            status_t status = setAudioPortConfig(&config);
+            reply->writeInt32(status);
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 9bb4a49..eee72c5 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -57,7 +57,14 @@
     QUERY_DEFAULT_PRE_PROCESSING,
     SET_EFFECT_ENABLED,
     IS_STREAM_ACTIVE_REMOTELY,
-    IS_OFFLOAD_SUPPORTED
+    IS_OFFLOAD_SUPPORTED,
+    LIST_AUDIO_PORTS,
+    GET_AUDIO_PORT,
+    CREATE_AUDIO_PATCH,
+    RELEASE_AUDIO_PATCH,
+    LIST_AUDIO_PATCHES,
+    SET_AUDIO_PORT_CONFIG,
+    REGISTER_CLIENT
 };
 
 class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
@@ -390,7 +397,141 @@
         data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
         data.write(&info, sizeof(audio_offload_info_t));
         remote()->transact(IS_OFFLOAD_SUPPORTED, data, &reply);
-        return reply.readInt32();    }
+        return reply.readInt32();
+    }
+
+    virtual status_t listAudioPorts(audio_port_role_t role,
+                                    audio_port_type_t type,
+                                    unsigned int *num_ports,
+                                    struct audio_port *ports,
+                                    unsigned int *generation)
+    {
+        if (num_ports == NULL || (*num_ports != 0 && ports == NULL) ||
+                generation == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        unsigned int numPortsReq = (ports == NULL) ? 0 : *num_ports;
+        data.writeInt32(role);
+        data.writeInt32(type);
+        data.writeInt32(numPortsReq);
+        status_t status = remote()->transact(LIST_AUDIO_PORTS, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+            *num_ports = (unsigned int)reply.readInt32();
+        }
+        ALOGI("listAudioPorts() status %d got *num_ports %d", status, *num_ports);
+        if (status == NO_ERROR) {
+            if (numPortsReq > *num_ports) {
+                numPortsReq = *num_ports;
+            }
+            if (numPortsReq > 0) {
+                reply.read(ports, numPortsReq * sizeof(struct audio_port));
+            }
+            *generation = reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t getAudioPort(struct audio_port *port)
+    {
+        if (port == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        data.write(port, sizeof(struct audio_port));
+        status_t status = remote()->transact(GET_AUDIO_PORT, data, &reply);
+        if (status != NO_ERROR ||
+                (status = (status_t)reply.readInt32()) != NO_ERROR) {
+            return status;
+        }
+        reply.read(port, sizeof(struct audio_port));
+        return status;
+    }
+
+    virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle)
+    {
+        if (patch == NULL || handle == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        data.write(patch, sizeof(struct audio_patch));
+        data.write(handle, sizeof(audio_patch_handle_t));
+        status_t status = remote()->transact(CREATE_AUDIO_PATCH, data, &reply);
+        if (status != NO_ERROR ||
+                (status = (status_t)reply.readInt32()) != NO_ERROR) {
+            return status;
+        }
+        reply.read(handle, sizeof(audio_patch_handle_t));
+        return status;
+    }
+
+    virtual status_t releaseAudioPatch(audio_patch_handle_t handle)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        data.write(&handle, sizeof(audio_patch_handle_t));
+        status_t status = remote()->transact(RELEASE_AUDIO_PATCH, data, &reply);
+        if (status != NO_ERROR) {
+            status = (status_t)reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t listAudioPatches(unsigned int *num_patches,
+                                      struct audio_patch *patches,
+                                      unsigned int *generation)
+    {
+        if (num_patches == NULL || (*num_patches != 0 && patches == NULL) ||
+                generation == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        unsigned int numPatchesReq = (patches == NULL) ? 0 : *num_patches;
+        data.writeInt32(numPatchesReq);
+        status_t status = remote()->transact(LIST_AUDIO_PATCHES, data, &reply);
+        if (status == NO_ERROR) {
+            status = (status_t)reply.readInt32();
+            *num_patches = (unsigned int)reply.readInt32();
+        }
+        if (status == NO_ERROR) {
+            if (numPatchesReq > *num_patches) {
+                numPatchesReq = *num_patches;
+            }
+            if (numPatchesReq > 0) {
+                reply.read(patches, numPatchesReq * sizeof(struct audio_patch));
+            }
+            *generation = reply.readInt32();
+        }
+        return status;
+    }
+
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config)
+    {
+        if (config == NULL) {
+            return BAD_VALUE;
+        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        data.write(config, sizeof(struct audio_port_config));
+        status_t status = remote()->transact(SET_AUDIO_PORT_CONFIG, data, &reply);
+        if (status != NO_ERROR) {
+            status = (status_t)reply.readInt32();
+        }
+        return status;
+    }
+    virtual void registerClient(const sp<IAudioPolicyServiceClient>& client)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        data.writeStrongBinder(client->asBinder());
+        remote()->transact(REGISTER_CLIENT, data, &reply);
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -687,6 +828,104 @@
             return NO_ERROR;
         }
 
+        case LIST_AUDIO_PORTS: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            audio_port_role_t role = (audio_port_role_t)data.readInt32();
+            audio_port_type_t type = (audio_port_type_t)data.readInt32();
+            unsigned int numPortsReq = data.readInt32();
+            unsigned int numPorts = numPortsReq;
+            unsigned int generation;
+            struct audio_port *ports =
+                    (struct audio_port *)calloc(numPortsReq, sizeof(struct audio_port));
+            status_t status = listAudioPorts(role, type, &numPorts, ports, &generation);
+            reply->writeInt32(status);
+            reply->writeInt32(numPorts);
+            ALOGI("LIST_AUDIO_PORTS status %d got numPorts %d", status, numPorts);
+
+            if (status == NO_ERROR) {
+                if (numPortsReq > numPorts) {
+                    numPortsReq = numPorts;
+                }
+                reply->write(ports, numPortsReq * sizeof(struct audio_port));
+                reply->writeInt32(generation);
+            }
+            free(ports);
+            return NO_ERROR;
+        }
+
+        case GET_AUDIO_PORT: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            struct audio_port port;
+            data.read(&port, sizeof(struct audio_port));
+            status_t status = getAudioPort(&port);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->write(&port, sizeof(struct audio_port));
+            }
+            return NO_ERROR;
+        }
+
+        case CREATE_AUDIO_PATCH: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            struct audio_patch patch;
+            data.read(&patch, sizeof(struct audio_patch));
+            audio_patch_handle_t handle;
+            data.read(&handle, sizeof(audio_patch_handle_t));
+            status_t status = createAudioPatch(&patch, &handle);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->write(&handle, sizeof(audio_patch_handle_t));
+            }
+            return NO_ERROR;
+        }
+
+        case RELEASE_AUDIO_PATCH: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            audio_patch_handle_t handle;
+            data.read(&handle, sizeof(audio_patch_handle_t));
+            status_t status = releaseAudioPatch(handle);
+            reply->writeInt32(status);
+            return NO_ERROR;
+        }
+
+        case LIST_AUDIO_PATCHES: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            unsigned int numPatchesReq = data.readInt32();
+            unsigned int numPatches = numPatchesReq;
+            unsigned int generation;
+            struct audio_patch *patches =
+                    (struct audio_patch *)calloc(numPatchesReq,
+                                                 sizeof(struct audio_patch));
+            status_t status = listAudioPatches(&numPatches, patches, &generation);
+            reply->writeInt32(status);
+            reply->writeInt32(numPatches);
+            if (status == NO_ERROR) {
+                if (numPatchesReq > numPatches) {
+                    numPatchesReq = numPatches;
+                }
+                reply->write(patches, numPatchesReq * sizeof(struct audio_patch));
+                reply->writeInt32(generation);
+            }
+            free(patches);
+            return NO_ERROR;
+        }
+
+        case SET_AUDIO_PORT_CONFIG: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            struct audio_port_config config;
+            data.read(&config, sizeof(struct audio_port_config));
+            status_t status = setAudioPortConfig(&config);
+            reply->writeInt32(status);
+            return NO_ERROR;
+        }
+        case REGISTER_CLIENT: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            sp<IAudioPolicyServiceClient> client = interface_cast<IAudioPolicyServiceClient>(
+                    data.readStrongBinder());
+            registerClient(client);
+            return NO_ERROR;
+        } break;
+
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/IAudioPolicyServiceClient.cpp b/media/libmedia/IAudioPolicyServiceClient.cpp
new file mode 100644
index 0000000..e802277
--- /dev/null
+++ b/media/libmedia/IAudioPolicyServiceClient.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IAudioPolicyServiceClient"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+
+#include <media/IAudioPolicyServiceClient.h>
+#include <media/AudioSystem.h>
+
+namespace android {
+
+enum {
+    PORT_LIST_UPDATE = IBinder::FIRST_CALL_TRANSACTION,
+    PATCH_LIST_UPDATE
+};
+
+class BpAudioPolicyServiceClient : public BpInterface<IAudioPolicyServiceClient>
+{
+public:
+    BpAudioPolicyServiceClient(const sp<IBinder>& impl)
+        : BpInterface<IAudioPolicyServiceClient>(impl)
+    {
+    }
+
+    void onAudioPortListUpdate()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
+        remote()->transact(PORT_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
+    void onAudioPatchListUpdate()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
+        remote()->transact(PATCH_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(AudioPolicyServiceClient, "android.media.IAudioPolicyServiceClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnAudioPolicyServiceClient::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch (code) {
+    case PORT_LIST_UPDATE: {
+            CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
+            onAudioPortListUpdate();
+            return NO_ERROR;
+        } break;
+    case PATCH_LIST_UPDATE: {
+            CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
+            onAudioPatchListUpdate();
+            return NO_ERROR;
+        } break;
+    default:
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 9c13848..5df232f 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -65,7 +65,7 @@
     virtual bool livesLocally(node_id node, pid_t pid) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(pid);
         remote()->transact(LIVES_LOCALLY, data, &reply);
 
@@ -104,7 +104,7 @@
 
         status_t err = reply.readInt32();
         if (err == OK) {
-            *node = (void*)reply.readIntPtr();
+            *node = (node_id)reply.readInt32();
         } else {
             *node = 0;
         }
@@ -115,7 +115,7 @@
     virtual status_t freeNode(node_id node) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         remote()->transact(FREE_NODE, data, &reply);
 
         return reply.readInt32();
@@ -125,7 +125,7 @@
             node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(cmd);
         data.writeInt32(param);
         remote()->transact(SEND_COMMAND, data, &reply);
@@ -138,7 +138,7 @@
             void *params, size_t size) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(index);
         data.writeInt64(size);
         data.write(params, size);
@@ -159,7 +159,7 @@
             const void *params, size_t size) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(index);
         data.writeInt64(size);
         data.write(params, size);
@@ -173,7 +173,7 @@
             void *params, size_t size) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(index);
         data.writeInt64(size);
         data.write(params, size);
@@ -194,7 +194,7 @@
             const void *params, size_t size) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(index);
         data.writeInt64(size);
         data.write(params, size);
@@ -207,7 +207,7 @@
             node_id node, OMX_STATETYPE* state) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         remote()->transact(GET_STATE, data, &reply);
 
         *state = static_cast<OMX_STATETYPE>(reply.readInt32());
@@ -218,7 +218,7 @@
             node_id node, OMX_U32 port_index, OMX_BOOL enable) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         data.writeInt32((uint32_t)enable);
         remote()->transact(ENABLE_GRAPHIC_BUFFERS, data, &reply);
@@ -231,7 +231,7 @@
             node_id node, OMX_U32 port_index, OMX_U32* usage) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         remote()->transact(GET_GRAPHIC_BUFFER_USAGE, data, &reply);
 
@@ -245,7 +245,7 @@
             buffer_id *buffer) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         data.writeStrongBinder(params->asBinder());
         remote()->transact(USE_BUFFER, data, &reply);
@@ -257,7 +257,7 @@
             return err;
         }
 
-        *buffer = (void*)reply.readIntPtr();
+        *buffer = (buffer_id)reply.readInt32();
 
         return err;
     }
@@ -268,7 +268,7 @@
             const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         data.write(*graphicBuffer);
         remote()->transact(USE_GRAPHIC_BUFFER, data, &reply);
@@ -280,7 +280,7 @@
             return err;
         }
 
-        *buffer = (void*)reply.readIntPtr();
+        *buffer = (buffer_id)reply.readInt32();
 
         return err;
     }
@@ -290,10 +290,10 @@
             const sp<GraphicBuffer> &graphicBuffer, buffer_id buffer) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         data.write(*graphicBuffer);
-        data.writeIntPtr((intptr_t)buffer);
+        data.writeInt32((int32_t)buffer);
         remote()->transact(UPDATE_GRAPHIC_BUFFER_IN_META, data, &reply);
 
         status_t err = reply.readInt32();
@@ -306,7 +306,7 @@
         Parcel data, reply;
         status_t err;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         err = remote()->transact(CREATE_INPUT_SURFACE, data, &reply);
         if (err != OK) {
@@ -329,7 +329,7 @@
         Parcel data, reply;
         status_t err;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         err = remote()->transact(SIGNAL_END_OF_INPUT_STREAM, data, &reply);
         if (err != OK) {
             ALOGW("binder transaction failed: %d", err);
@@ -343,7 +343,7 @@
             node_id node, OMX_U32 port_index, OMX_BOOL enable) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         data.writeInt32((uint32_t)enable);
         remote()->transact(STORE_META_DATA_IN_BUFFERS, data, &reply);
@@ -357,7 +357,7 @@
             OMX_U32 max_width, OMX_U32 max_height) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         data.writeInt32((int32_t)enable);
         data.writeInt32(max_width);
@@ -373,7 +373,7 @@
             buffer_id *buffer, void **buffer_data) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         data.writeInt64(size);
         remote()->transact(ALLOC_BUFFER, data, &reply);
@@ -385,8 +385,8 @@
             return err;
         }
 
-        *buffer = (void *)reply.readIntPtr();
-        *buffer_data = (void *)reply.readIntPtr();
+        *buffer = (buffer_id)reply.readInt32();
+        *buffer_data = (void *)reply.readInt64();
 
         return err;
     }
@@ -396,7 +396,7 @@
             buffer_id *buffer) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         data.writeStrongBinder(params->asBinder());
         remote()->transact(ALLOC_BUFFER_WITH_BACKUP, data, &reply);
@@ -408,7 +408,7 @@
             return err;
         }
 
-        *buffer = (void*)reply.readIntPtr();
+        *buffer = (buffer_id)reply.readInt32();
 
         return err;
     }
@@ -417,9 +417,9 @@
             node_id node, OMX_U32 port_index, buffer_id buffer) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
-        data.writeIntPtr((intptr_t)buffer);
+        data.writeInt32((int32_t)buffer);
         remote()->transact(FREE_BUFFER, data, &reply);
 
         return reply.readInt32();
@@ -428,8 +428,8 @@
     virtual status_t fillBuffer(node_id node, buffer_id buffer) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
-        data.writeIntPtr((intptr_t)buffer);
+        data.writeInt32((int32_t)node);
+        data.writeInt32((int32_t)buffer);
         remote()->transact(FILL_BUFFER, data, &reply);
 
         return reply.readInt32();
@@ -442,8 +442,8 @@
             OMX_U32 flags, OMX_TICKS timestamp) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
-        data.writeIntPtr((intptr_t)buffer);
+        data.writeInt32((int32_t)node);
+        data.writeInt32((int32_t)buffer);
         data.writeInt32(range_offset);
         data.writeInt32(range_length);
         data.writeInt32(flags);
@@ -459,7 +459,7 @@
             OMX_INDEXTYPE *index) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeCString(parameter_name);
 
         remote()->transact(GET_EXTENSION_INDEX, data, &reply);
@@ -482,7 +482,7 @@
             size_t size) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeIntPtr((intptr_t)node);
+        data.writeInt32((int32_t)node);
         data.writeInt32(port_index);
         data.writeInt64(size);
         data.write(optionData, size);
@@ -509,7 +509,7 @@
         case LIVES_LOCALLY:
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
-            node_id node = (void *)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             pid_t pid = (pid_t)data.readInt32();
             reply->writeInt32(livesLocally(node, pid));
 
@@ -553,7 +553,7 @@
             status_t err = allocateNode(name, observer, &node);
             reply->writeInt32(err);
             if (err == OK) {
-                reply->writeIntPtr((intptr_t)node);
+                reply->writeInt32((int32_t)node);
             }
 
             return NO_ERROR;
@@ -563,7 +563,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
 
             reply->writeInt32(freeNode(node));
 
@@ -574,7 +574,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
 
             OMX_COMMANDTYPE cmd =
                 static_cast<OMX_COMMANDTYPE>(data.readInt32());
@@ -593,7 +593,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
 
             size_t size = data.readInt64();
@@ -644,7 +644,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_STATETYPE state = OMX_StateInvalid;
 
             status_t err = getState(node, &state);
@@ -658,7 +658,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
             OMX_BOOL enable = (OMX_BOOL)data.readInt32();
 
@@ -672,7 +672,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
 
             OMX_U32 usage = 0;
@@ -687,7 +687,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
             sp<IMemory> params =
                 interface_cast<IMemory>(data.readStrongBinder());
@@ -697,7 +697,7 @@
             reply->writeInt32(err);
 
             if (err == OK) {
-                reply->writeIntPtr((intptr_t)buffer);
+                reply->writeInt32((int32_t)buffer);
             }
 
             return NO_ERROR;
@@ -707,7 +707,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
             sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
             data.read(*graphicBuffer);
@@ -718,7 +718,7 @@
             reply->writeInt32(err);
 
             if (err == OK) {
-                reply->writeIntPtr((intptr_t)buffer);
+                reply->writeInt32((int32_t)buffer);
             }
 
             return NO_ERROR;
@@ -728,11 +728,11 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
             sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
             data.read(*graphicBuffer);
-            buffer_id buffer = (void*)data.readIntPtr();
+            buffer_id buffer = (buffer_id)data.readInt32();
 
             status_t err = updateGraphicBufferInMeta(
                     node, port_index, graphicBuffer, buffer);
@@ -745,7 +745,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
 
             sp<IGraphicBufferProducer> bufferProducer;
@@ -765,7 +765,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
 
             status_t err = signalEndOfInputStream(node);
             reply->writeInt32(err);
@@ -777,7 +777,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
             OMX_BOOL enable = (OMX_BOOL)data.readInt32();
 
@@ -791,7 +791,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
             OMX_BOOL enable = (OMX_BOOL)data.readInt32();
             OMX_U32 max_width = data.readInt32();
@@ -808,7 +808,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
             size_t size = data.readInt64();
 
@@ -819,8 +819,8 @@
             reply->writeInt32(err);
 
             if (err == OK) {
-                reply->writeIntPtr((intptr_t)buffer);
-                reply->writeIntPtr((intptr_t)buffer_data);
+                reply->writeInt32((int32_t)buffer);
+                reply->writeInt64((uintptr_t)buffer_data);
             }
 
             return NO_ERROR;
@@ -830,7 +830,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
             sp<IMemory> params =
                 interface_cast<IMemory>(data.readStrongBinder());
@@ -842,7 +842,7 @@
             reply->writeInt32(err);
 
             if (err == OK) {
-                reply->writeIntPtr((intptr_t)buffer);
+                reply->writeInt32((int32_t)buffer);
             }
 
             return NO_ERROR;
@@ -852,9 +852,9 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             OMX_U32 port_index = data.readInt32();
-            buffer_id buffer = (void*)data.readIntPtr();
+            buffer_id buffer = (buffer_id)data.readInt32();
             reply->writeInt32(freeBuffer(node, port_index, buffer));
 
             return NO_ERROR;
@@ -864,8 +864,8 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
-            buffer_id buffer = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
+            buffer_id buffer = (buffer_id)data.readInt32();
             reply->writeInt32(fillBuffer(node, buffer));
 
             return NO_ERROR;
@@ -875,8 +875,8 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
-            buffer_id buffer = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
+            buffer_id buffer = (buffer_id)data.readInt32();
             OMX_U32 range_offset = data.readInt32();
             OMX_U32 range_length = data.readInt32();
             OMX_U32 flags = data.readInt32();
@@ -894,7 +894,7 @@
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
 
-            node_id node = (void*)data.readIntPtr();
+            node_id node = (node_id)data.readInt32();
             const char *parameter_name = data.readCString();
 
             OMX_INDEXTYPE index;
@@ -927,6 +927,8 @@
         data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor());
         data.write(&msg, sizeof(msg));
 
+        ALOGV("onMessage writing message %d, size %zu", msg.type, sizeof(msg));
+
         remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY);
     }
 };
@@ -943,6 +945,8 @@
             omx_message msg;
             data.read(&msg, sizeof(msg));
 
+            ALOGV("onTransact reading message %d, size %zu", msg.type, sizeof(msg));
+
             // XXX Could use readInplace maybe?
             onMessage(msg);
 
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/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index d8d939a..857e703 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1376,16 +1376,15 @@
 
             sp<NuPlayerDriver> driver = mDriver.promote();
             if (driver != NULL) {
+                // notify duration first, so that it's definitely set when
+                // the app received the "prepare complete" callback.
+                int64_t durationUs;
+                if (mSource->getDuration(&durationUs) == OK) {
+                    driver->notifyDuration(durationUs);
+                }
                 driver->notifyPrepareCompleted(err);
             }
 
-            int64_t durationUs;
-            if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
-                sp<NuPlayerDriver> driver = mDriver.promote();
-                if (driver != NULL) {
-                    driver->notifyDuration(durationUs);
-                }
-            }
             break;
         }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index e4850f0..280b5af 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -284,6 +284,10 @@
         case STATE_PREPARED:
         {
             mStartupSeekTimeUs = seekTimeUs;
+            // pretend that the seek completed. It will actually happen when starting playback.
+            // TODO: actually perform the seek here, so the player is ready to go at the new
+            // location
+            notifySeekComplete();
             break;
         }
 
diff --git a/media/libnbaio/AudioStreamInSource.cpp b/media/libnbaio/AudioStreamInSource.cpp
index af8f365..6aab48a 100644
--- a/media/libnbaio/AudioStreamInSource.cpp
+++ b/media/libnbaio/AudioStreamInSource.cpp
@@ -46,7 +46,8 @@
         uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common);
         audio_channel_mask_t channelMask =
                 (audio_channel_mask_t) mStream->common.get_channels(&mStream->common);
-        mFormat = Format_from_SR_C(sampleRate, popcount(channelMask), streamFormat);
+        mFormat = Format_from_SR_C(sampleRate,
+                audio_channel_count_from_in_mask(channelMask), streamFormat);
         mFrameSize = Format_frameSize(mFormat);
     }
     return NBAIO_Source::negotiate(offers, numOffers, counterOffers, numCounterOffers);
diff --git a/media/libnbaio/AudioStreamOutSink.cpp b/media/libnbaio/AudioStreamOutSink.cpp
index c28d34d..0d5f935 100644
--- a/media/libnbaio/AudioStreamOutSink.cpp
+++ b/media/libnbaio/AudioStreamOutSink.cpp
@@ -43,7 +43,8 @@
         uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common);
         audio_channel_mask_t channelMask =
                 (audio_channel_mask_t) mStream->common.get_channels(&mStream->common);
-        mFormat = Format_from_SR_C(sampleRate, popcount(channelMask), streamFormat);
+        mFormat = Format_from_SR_C(sampleRate,
+                audio_channel_count_from_out_mask(channelMask), streamFormat);
         mFrameSize = Format_frameSize(mFormat);
     }
     return NBAIO_Sink::negotiate(offers, numOffers, counterOffers, numCounterOffers);
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 0a3a3b6..d3c508d 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -17,6 +17,11 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "ACodec"
 
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
+#include <inttypes.h>
 #include <utils/Trace.h>
 
 #include <media/stagefright/ACodec.h>
@@ -67,7 +72,7 @@
         sp<AMessage> msg = mNotify->dup();
 
         msg->setInt32("type", omx_msg.type);
-        msg->setPointer("node", omx_msg.node);
+        msg->setInt32("node", omx_msg.node);
 
         switch (omx_msg.type) {
             case omx_message::EVENT:
@@ -80,13 +85,13 @@
 
             case omx_message::EMPTY_BUFFER_DONE:
             {
-                msg->setPointer("buffer", omx_msg.u.buffer_data.buffer);
+                msg->setInt32("buffer", omx_msg.u.buffer_data.buffer);
                 break;
             }
 
             case omx_message::FILL_BUFFER_DONE:
             {
-                msg->setPointer(
+                msg->setInt32(
                         "buffer", omx_msg.u.extended_buffer_data.buffer);
                 msg->setInt32(
                         "range_offset",
@@ -355,7 +360,7 @@
 
 ACodec::ACodec()
     : mQuirks(0),
-      mNode(NULL),
+      mNode(0),
       mSentFormat(false),
       mIsEncoder(false),
       mUseMetadataOnEncoderOutput(false),
@@ -370,8 +375,8 @@
       mMetaDataBuffersToSubmit(0),
       mRepeatFrameDelayUs(-1ll),
       mMaxPtsGapUs(-1ll),
-      mTimePerCaptureUs(-1ll),
       mTimePerFrameUs(-1ll),
+      mTimePerCaptureUs(-1ll),
       mCreateInputBuffersSuspended(false) {
     mUninitializedState = new UninitializedState(this);
     mLoadedState = new LoadedState(this);
@@ -488,7 +493,7 @@
                 mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
 
         if (err == OK) {
-            ALOGV("[%s] Allocating %lu buffers of size %lu on %s port",
+            ALOGV("[%s] Allocating %u buffers of size %u on %s port",
                     mComponentName.c_str(),
                     def.nBufferCountActual, def.nBufferSize,
                     portIndex == kPortIndexInput ? "input" : "output");
@@ -662,7 +667,7 @@
             break;
         }
 
-        ALOGW("[%s] setting nBufferCountActual to %lu failed: %d",
+        ALOGW("[%s] setting nBufferCountActual to %u failed: %d",
                 mComponentName.c_str(), newBufferCount, err);
         /* exit condition */
         if (extraBuffers == 0) {
@@ -692,7 +697,7 @@
         return err;
     mNumUndequeuedBuffers = minUndequeuedBuffers;
 
-    ALOGV("[%s] Allocating %lu buffers from a native window of size %lu on "
+    ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
          "output port",
          mComponentName.c_str(), bufferCount, bufferSize);
 
@@ -716,14 +721,14 @@
         err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
                 &bufferId);
         if (err != 0) {
-            ALOGE("registering GraphicBuffer %lu with OMX IL component failed: "
+            ALOGE("registering GraphicBuffer %u with OMX IL component failed: "
                  "%d", i, err);
             break;
         }
 
         mBuffers[kPortIndexOutput].editItemAt(i).mBufferID = bufferId;
 
-        ALOGV("[%s] Registered graphic buffer with ID %p (pointer = %p)",
+        ALOGV("[%s] Registered graphic buffer with ID %u (pointer = %p)",
              mComponentName.c_str(),
              bufferId, graphicBuffer.get());
     }
@@ -758,7 +763,7 @@
         return err;
     mNumUndequeuedBuffers = minUndequeuedBuffers;
 
-    ALOGV("[%s] Allocating %lu meta buffers on output port",
+    ALOGV("[%s] Allocating %u meta buffers on output port",
          mComponentName.c_str(), bufferCount);
 
     size_t totalSize = bufferCount * 8;
@@ -782,7 +787,7 @@
 
         mBuffers[kPortIndexOutput].push(info);
 
-        ALOGV("[%s] allocated meta buffer with ID %p (pointer = %p)",
+        ALOGV("[%s] allocated meta buffer with ID %u (pointer = %p)",
              mComponentName.c_str(), info.mBufferID, mem->pointer());
     }
 
@@ -799,7 +804,7 @@
     if (info == NULL)
         return ERROR_IO;
 
-    ALOGV("[%s] submitting output meta buffer ID %p for graphic buffer %p",
+    ALOGV("[%s] submitting output meta buffer ID %u for graphic buffer %p",
           mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer.get());
 
     --mMetaDataBuffersToSubmit;
@@ -813,7 +818,7 @@
 status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) {
     CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);
 
-    ALOGV("[%s] Calling cancelBuffer on buffer %p",
+    ALOGV("[%s] Calling cancelBuffer on buffer %u",
          mComponentName.c_str(), info->mBufferID);
 
     int err = mNativeWindow->cancelBuffer(
@@ -985,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,
@@ -1806,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 },
@@ -2611,7 +2619,7 @@
 
         if (info->mStatus != BufferInfo::OWNED_BY_US
                 && info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) {
-            ALOGV("[%s] Buffer %p on port %ld still has status %d",
+            ALOGV("[%s] Buffer %u on port %u still has status %d",
                     mComponentName.c_str(),
                     info->mBufferID, portIndex, info->mStatus);
             return false;
@@ -2864,6 +2872,24 @@
                     break;
                 }
 
+                case OMX_AUDIO_CodingAndroidOPUS:
+                {
+                    OMX_AUDIO_PARAM_ANDROID_OPUSTYPE params;
+                    InitOMXParams(&params);
+                    params.nPortIndex = portIndex;
+
+                    CHECK_EQ((status_t)OK, mOMX->getParameter(
+                            mNode,
+                            (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidOpus,
+                            &params,
+                            sizeof(params)));
+
+                    notify->setString("mime", MEDIA_MIMETYPE_AUDIO_OPUS);
+                    notify->setInt32("channel-count", params.nChannels);
+                    notify->setInt32("sample-rate", params.nSampleRate);
+                    break;
+                }
+
                 default:
                     ALOGE("UNKNOWN AUDIO CODING: %d\n", audioDef->eEncoding);
                     TRESPASS();
@@ -3177,7 +3203,7 @@
     }
 
     IOMX::node_id nodeID;
-    CHECK(msg->findPointer("node", &nodeID));
+    CHECK(msg->findInt32("node", (int32_t*)&nodeID));
     CHECK_EQ(nodeID, mCodec->mNode);
 
     switch (type) {
@@ -3208,7 +3234,7 @@
         case omx_message::EMPTY_BUFFER_DONE:
         {
             IOMX::buffer_id bufferID;
-            CHECK(msg->findPointer("buffer", &bufferID));
+            CHECK(msg->findInt32("buffer", (int32_t*)&bufferID));
 
             return onOMXEmptyBufferDone(bufferID);
         }
@@ -3216,7 +3242,7 @@
         case omx_message::FILL_BUFFER_DONE:
         {
             IOMX::buffer_id bufferID;
-            CHECK(msg->findPointer("buffer", &bufferID));
+            CHECK(msg->findInt32("buffer", (int32_t*)&bufferID));
 
             int32_t rangeOffset, rangeLength, flags;
             int64_t timeUs;
@@ -3313,13 +3339,13 @@
 
     sp<AMessage> notify = mCodec->mNotify->dup();
     notify->setInt32("what", ACodec::kWhatFillThisBuffer);
-    notify->setPointer("buffer-id", info->mBufferID);
+    notify->setInt32("buffer-id", info->mBufferID);
 
     info->mData->meta()->clear();
     notify->setBuffer("buffer", info->mData);
 
     sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id());
-    reply->setPointer("buffer-id", info->mBufferID);
+    reply->setInt32("buffer-id", info->mBufferID);
 
     notify->setMessage("reply", reply);
 
@@ -3330,8 +3356,7 @@
 
 void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
     IOMX::buffer_id bufferID;
-    CHECK(msg->findPointer("buffer-id", &bufferID));
-
+    CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
     sp<ABuffer> buffer;
     int32_t err = OK;
     bool eos = false;
@@ -3530,7 +3555,7 @@
         size_t rangeOffset, size_t rangeLength,
         OMX_U32 flags,
         int64_t timeUs) {
-    ALOGV("[%s] onOMXFillBufferDone %p time %lld us, flags = 0x%08lx",
+    ALOGV("[%s] onOMXFillBufferDone %u time %" PRId64 " us, flags = 0x%08x",
          mCodec->mComponentName.c_str(), bufferID, timeUs, flags);
 
     ssize_t index;
@@ -3567,7 +3592,7 @@
         case RESUBMIT_BUFFERS:
         {
             if (rangeLength == 0 && !(flags & OMX_BUFFERFLAG_EOS)) {
-                ALOGV("[%s] calling fillBuffer %p",
+                ALOGV("[%s] calling fillBuffer %u",
                      mCodec->mComponentName.c_str(), info->mBufferID);
 
                 CHECK_EQ(mCodec->mOMX->fillBuffer(
@@ -3609,11 +3634,11 @@
 
             sp<AMessage> notify = mCodec->mNotify->dup();
             notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
-            notify->setPointer("buffer-id", info->mBufferID);
+            notify->setInt32("buffer-id", info->mBufferID);
             notify->setBuffer("buffer", info->mData);
             notify->setInt32("flags", flags);
 
-            reply->setPointer("buffer-id", info->mBufferID);
+            reply->setInt32("buffer-id", info->mBufferID);
 
             notify->setMessage("reply", reply);
 
@@ -3649,8 +3674,7 @@
 
 void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
     IOMX::buffer_id bufferID;
-    CHECK(msg->findPointer("buffer-id", &bufferID));
-
+    CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
     ssize_t index;
     BufferInfo *info =
         mCodec->findBufferByID(kPortIndexOutput, bufferID, &index);
@@ -3678,7 +3702,7 @@
             // API 20.  Perhaps check for target SDK version.
 #if 0
             if (info->mData->meta()->findInt64("timeUs", &timestampNs)) {
-                ALOGI("using buffer PTS of %" PRId64, timestampNs);
+                ALOGV("using buffer PTS of %" PRId64, timestampNs);
                 timestampNs *= 1000;
             }
 #endif
@@ -3688,8 +3712,6 @@
         err = native_window_set_buffers_timestamp(mCodec->mNativeWindow.get(), timestampNs);
         if (err != OK) {
             ALOGW("failed to set buffer timestamp: %d", err);
-        } else {
-            ALOGI("set PTS to %" PRId64, timestampNs);
         }
 
         if ((err = mCodec->mNativeWindow->queueBuffer(
@@ -3735,7 +3757,7 @@
                 }
 
                 if (info != NULL) {
-                    ALOGV("[%s] calling fillBuffer %p",
+                    ALOGV("[%s] calling fillBuffer %u",
                          mCodec->mComponentName.c_str(), info->mBufferID);
 
                     CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
@@ -5008,7 +5030,7 @@
         {
             sp<AMessage> msg = new AMessage(kWhatOMXMessage, mCodec->id());
             msg->setInt32("type", omx_message::EVENT);
-            msg->setPointer("node", mCodec->mNode);
+            msg->setInt32("node", mCodec->mNode);
             msg->setInt32("event", event);
             msg->setInt32("data1", data1);
             msg->setInt32("data2", data2);
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/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 5b525f2..b9c5904 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1555,8 +1555,8 @@
         int32_t portIndex, const sp<AMessage> &msg) {
     CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
 
-    void *bufferID;
-    CHECK(msg->findPointer("buffer-id", &bufferID));
+    uint32_t bufferID;
+    CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
 
     Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
 
@@ -1728,7 +1728,7 @@
             // API 20.  Perhaps check for target SDK version.
 #if 0
             if (info->mData->meta()->findInt64("timeUs", &timestampNs)) {
-                ALOGI("using buffer PTS of %" PRId64, timestampNs);
+                ALOGV("using buffer PTS of %" PRId64, timestampNs);
                 timestampNs *= 1000;
             }
 #endif
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/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 9f9352d..aca21cf 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -16,6 +16,11 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "OMXClient"
+
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
 #include <utils/Log.h>
 
 #include <binder/IServiceManager.h>
@@ -141,7 +146,7 @@
     const sp<IOMX> &getOMX(node_id node) const;
     const sp<IOMX> &getOMX_l(node_id node) const;
 
-    static bool IsSoftwareComponent(const char *name);
+    static bool CanLiveLocally(const char *name);
 
     DISALLOW_EVIL_CONSTRUCTORS(MuxOMX);
 };
@@ -164,8 +169,15 @@
 }
 
 // static
-bool MuxOMX::IsSoftwareComponent(const char *name) {
+bool MuxOMX::CanLiveLocally(const char *name) {
+#ifdef __LP64__
+    (void)name; // disable unused parameter warning
+    // 64 bit processes always run OMX remote on MediaServer
+    return false;
+#else
+    // 32 bit processes run only OMX.google.* components locally
     return !strncasecmp(name, "OMX.google.", 11);
+#endif
 }
 
 const sp<IOMX> &MuxOMX::getOMX(node_id node) const {
@@ -197,7 +209,7 @@
 
     sp<IOMX> omx;
 
-    if (IsSoftwareComponent(name)) {
+    if (CanLiveLocally(name)) {
         if (mLocalOMX == NULL) {
             mLocalOMX = new OMX;
         }
@@ -382,7 +394,7 @@
     mOMX = service->getOMX();
     CHECK(mOMX.get() != NULL);
 
-    if (!mOMX->livesLocally(NULL /* node */, getpid())) {
+    if (!mOMX->livesLocally(0 /* node */, getpid())) {
         ALOGI("Using client-side OMX mux.");
         mOMX = new MuxOMX(mOMX);
     }
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 9a7f3db..c028dbf 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -18,6 +18,11 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "OMXCodec"
+
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
 #include <utils/Log.h>
 
 #include "include/AACEncoder.h"
@@ -130,6 +135,7 @@
 
 template<class T>
 static void InitOMXParams(T *params) {
+    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(OMX_PTR) == 4); // check OMX_PTR is 4 bytes.
     params->nSize = sizeof(T);
     params->nVersion.s.nVersionMajor = 1;
     params->nVersion.s.nVersionMinor = 0;
@@ -689,7 +695,7 @@
         // CHECK_EQ(format.nIndex, index);
 
 #if 1
-        CODEC_LOGV("portIndex: %ld, index: %ld, eCompressionFormat=%d eColorFormat=%d",
+        CODEC_LOGV("portIndex: %u, index: %u, eCompressionFormat=%d eColorFormat=%d",
              portIndex,
              index, format.eCompressionFormat, format.eColorFormat);
 #endif
@@ -791,7 +797,7 @@
         portFormat.nIndex = index;
 
         if (index >= kMaxColorFormatSupported) {
-            CODEC_LOGE("More than %ld color formats are supported???", index);
+            CODEC_LOGE("More than %u color formats are supported???", index);
             break;
         }
     }
@@ -1833,7 +1839,7 @@
             break;
         }
 
-        CODEC_LOGW("setting nBufferCountActual to %lu failed: %d",
+        CODEC_LOGW("setting nBufferCountActual to %u failed: %d",
                 newBufferCount, err);
         /* exit condition */
         if (extraBuffers == 0) {
@@ -1851,7 +1857,7 @@
         return err;
     }
 
-    CODEC_LOGV("allocating %lu buffers from a native window of size %lu on "
+    CODEC_LOGV("allocating %u buffers from a native window of size %u on "
             "output port", def.nBufferCountActual, def.nBufferSize);
 
     // Dequeue buffers and send them to OMX
@@ -1884,7 +1890,7 @@
 
         mPortBuffers[kPortIndexOutput].editItemAt(i).mBuffer = bufferId;
 
-        CODEC_LOGV("registered graphic buffer with ID %p (pointer = %p)",
+        CODEC_LOGV("registered graphic buffer with ID %u (pointer = %p)",
                 bufferId, graphicBuffer.get());
     }
 
@@ -1911,7 +1917,7 @@
 
 status_t OMXCodec::cancelBufferToNativeWindow(BufferInfo *info) {
     CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US);
-    CODEC_LOGV("Calling cancelBuffer on buffer %p", info->mBuffer);
+    CODEC_LOGV("Calling cancelBuffer on buffer %u", info->mBuffer);
     int err = mNativeWindow->cancelBuffer(
         mNativeWindow.get(), info->mMediaBuffer->graphicBuffer().get(), -1);
     if (err != 0) {
@@ -2149,7 +2155,7 @@
         {
             IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
 
-            CODEC_LOGV("EMPTY_BUFFER_DONE(buffer: %p)", buffer);
+            CODEC_LOGV("EMPTY_BUFFER_DONE(buffer: %u)", buffer);
 
             Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
             size_t i = 0;
@@ -2159,7 +2165,7 @@
 
             CHECK(i < buffers->size());
             if ((*buffers)[i].mStatus != OWNED_BY_COMPONENT) {
-                ALOGW("We already own input buffer %p, yet received "
+                ALOGW("We already own input buffer %u, yet received "
                      "an EMPTY_BUFFER_DONE.", buffer);
             }
 
@@ -2173,7 +2179,7 @@
             }
 
             if (mPortStatus[kPortIndexInput] == DISABLING) {
-                CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
+                CODEC_LOGV("Port is disabled, freeing buffer %u", buffer);
 
                 status_t err = freeBuffer(kPortIndexInput, i);
                 CHECK_EQ(err, (status_t)OK);
@@ -2195,7 +2201,7 @@
             IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
             OMX_U32 flags = msg.u.extended_buffer_data.flags;
 
-            CODEC_LOGV("FILL_BUFFER_DONE(buffer: %p, size: %ld, flags: 0x%08lx, timestamp: %lld us (%.2f secs))",
+            CODEC_LOGV("FILL_BUFFER_DONE(buffer: %u, size: %u, flags: 0x%08x, timestamp: %lld us (%.2f secs))",
                  buffer,
                  msg.u.extended_buffer_data.range_length,
                  flags,
@@ -2212,14 +2218,14 @@
             BufferInfo *info = &buffers->editItemAt(i);
 
             if (info->mStatus != OWNED_BY_COMPONENT) {
-                ALOGW("We already own output buffer %p, yet received "
+                ALOGW("We already own output buffer %u, yet received "
                      "a FILL_BUFFER_DONE.", buffer);
             }
 
             info->mStatus = OWNED_BY_US;
 
             if (mPortStatus[kPortIndexOutput] == DISABLING) {
-                CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
+                CODEC_LOGV("Port is disabled, freeing buffer %u", buffer);
 
                 status_t err = freeBuffer(kPortIndexOutput, i);
                 CHECK_EQ(err, (status_t)OK);
@@ -2268,7 +2274,7 @@
                     buffer->meta_data()->setInt32(kKeyIsUnreadable, true);
                 }
 
-                buffer->meta_data()->setPointer(
+                buffer->meta_data()->setInt32(
                         kKeyBufferID,
                         msg.u.extended_buffer_data.buffer);
 
@@ -2410,7 +2416,7 @@
 
         case OMX_EventError:
         {
-            CODEC_LOGE("ERROR(0x%08lx, %ld)", data1, data2);
+            CODEC_LOGE("OMX_EventError(0x%08x, %u)", data1, data2);
 
             setState(ERROR);
             break;
@@ -2418,7 +2424,7 @@
 
         case OMX_EventPortSettingsChanged:
         {
-            CODEC_LOGV("OMX_EventPortSettingsChanged(port=%ld, data2=0x%08lx)",
+            CODEC_LOGV("OMX_EventPortSettingsChanged(port=%u, data2=0x%08x)",
                        data1, data2);
 
             if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
@@ -2458,7 +2464,7 @@
                         // The scale is in 16.16 format.
                         // scale 1.0 = 0x010000. When there is no
                         // need to change the display, skip it.
-                        ALOGV("Get OMX_IndexConfigScale: 0x%lx/0x%lx",
+                        ALOGV("Get OMX_IndexConfigScale: 0x%x/0x%x",
                                 scale.xWidth, scale.xHeight);
 
                         if (scale.xWidth != 0x010000) {
@@ -2492,7 +2498,7 @@
 
         default:
         {
-            CODEC_LOGV("EVENT(%d, %ld, %ld)", event, data1, data2);
+            CODEC_LOGV("EVENT(%d, %u, %u)", event, data1, data2);
             break;
         }
     }
@@ -2509,7 +2515,7 @@
         case OMX_CommandPortDisable:
         {
             OMX_U32 portIndex = data;
-            CODEC_LOGV("PORT_DISABLED(%ld)", portIndex);
+            CODEC_LOGV("PORT_DISABLED(%u)", portIndex);
 
             CHECK(mState == EXECUTING || mState == RECONFIGURING);
             CHECK_EQ((int)mPortStatus[portIndex], (int)DISABLING);
@@ -2533,7 +2539,7 @@
 
                 status_t err = enablePortAsync(portIndex);
                 if (err != OK) {
-                    CODEC_LOGE("enablePortAsync(%ld) failed (err = %d)", portIndex, err);
+                    CODEC_LOGE("enablePortAsync(%u) failed (err = %d)", portIndex, err);
                     setState(ERROR);
                 } else {
                     err = allocateBuffersOnPort(portIndex);
@@ -2554,7 +2560,7 @@
         case OMX_CommandPortEnable:
         {
             OMX_U32 portIndex = data;
-            CODEC_LOGV("PORT_ENABLED(%ld)", portIndex);
+            CODEC_LOGV("PORT_ENABLED(%u)", portIndex);
 
             CHECK(mState == EXECUTING || mState == RECONFIGURING);
             CHECK_EQ((int)mPortStatus[portIndex], (int)ENABLING);
@@ -2575,7 +2581,7 @@
         {
             OMX_U32 portIndex = data;
 
-            CODEC_LOGV("FLUSH_DONE(%ld)", portIndex);
+            CODEC_LOGV("FLUSH_DONE(%u)", portIndex);
 
             CHECK_EQ((int)mPortStatus[portIndex], (int)SHUTTING_DOWN);
             mPortStatus[portIndex] = ENABLED;
@@ -3893,7 +3899,7 @@
             return UNKNOWN_ERROR;
         }
 
-        CODEC_LOGV("seeking to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6);
+        CODEC_LOGV("seeking to %" PRId64 " us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6);
 
         mSignalledEOS = false;
 
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index 49ff238..afb00aa 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -3,7 +3,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-      SoftAAC2.cpp
+      SoftAAC2.cpp \
+      DrcPresModeWrap.cpp
 
 LOCAL_C_INCLUDES := \
       frameworks/av/media/libstagefright/include \
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
new file mode 100644
index 0000000..129ad65
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 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 "DrcPresModeWrap.h"
+
+#include <assert.h>
+
+#define LOG_TAG "SoftAAC2_DrcWrapper"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+//#define DRC_PRES_MODE_WRAP_DEBUG
+
+#define GPM_ENCODER_TARGET_LEVEL 64
+#define MAX_TARGET_LEVEL 64
+
+CDrcPresModeWrapper::CDrcPresModeWrapper()
+{
+    mDataUpdate = true;
+
+    /* Data from streamInfo. */
+    /* Initialized to the same values as in the aac decoder */
+    mStreamPRL = -1;
+    mStreamDRCPresMode = -1;
+    mStreamNrAACChan = 0;
+    mStreamNrOutChan = 0;
+
+    /* Desired values (set by user). */
+    /* Initialized to the same values as in the aac decoder */
+    mDesTarget = -1;
+    mDesAttFactor = 0;
+    mDesBoostFactor = 0;
+    mDesHeavy = 0;
+
+    mEncoderTarget = -1;
+
+    /* Values from last time. */
+    /* Initialized to the same values as the desired values */
+    mLastTarget = -1;
+    mLastAttFactor = 0;
+    mLastBoostFactor = 0;
+    mLastHeavy = 0;
+}
+
+CDrcPresModeWrapper::~CDrcPresModeWrapper()
+{
+}
+
+void
+CDrcPresModeWrapper::setDecoderHandle(const HANDLE_AACDECODER handle)
+{
+    mHandleDecoder = handle;
+}
+
+void
+CDrcPresModeWrapper::submitStreamData(CStreamInfo* pStreamInfo)
+{
+    assert(pStreamInfo);
+
+    if (mStreamPRL != pStreamInfo->drcProgRefLev) {
+        mStreamPRL = pStreamInfo->drcProgRefLev;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: drcProgRefLev is %d\n", mStreamPRL);
+#endif
+    }
+
+    if (mStreamDRCPresMode != pStreamInfo->drcPresMode) {
+        mStreamDRCPresMode = pStreamInfo->drcPresMode;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: drcPresMode is %d\n", mStreamDRCPresMode);
+#endif
+    }
+
+    if (mStreamNrAACChan != pStreamInfo->aacNumChannels) {
+        mStreamNrAACChan = pStreamInfo->aacNumChannels;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: aacNumChannels is %d\n", mStreamNrAACChan);
+#endif
+    }
+
+    if (mStreamNrOutChan != pStreamInfo->numChannels) {
+        mStreamNrOutChan = pStreamInfo->numChannels;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: numChannels is %d\n", mStreamNrOutChan);
+#endif
+    }
+
+
+
+    if (mStreamNrOutChan<mStreamNrAACChan) {
+        mIsDownmix = true;
+    } else {
+        mIsDownmix = false;
+    }
+
+    if (mIsDownmix && (mStreamNrOutChan == 1)) {
+        mIsMonoDownmix = true;
+    } else {
+        mIsMonoDownmix = false;
+    }
+
+    if (mIsDownmix && mStreamNrOutChan == 2){
+        mIsStereoDownmix = true;
+    } else {
+        mIsStereoDownmix = false;
+    }
+
+}
+
+void
+CDrcPresModeWrapper::setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value)
+{
+    switch (param) {
+    case DRC_PRES_MODE_WRAP_DESIRED_TARGET:
+        mDesTarget = value;
+        break;
+    case DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR:
+        mDesAttFactor = value;
+        break;
+    case DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR:
+        mDesBoostFactor = value;
+        break;
+    case DRC_PRES_MODE_WRAP_DESIRED_HEAVY:
+        mDesHeavy = value;
+        break;
+    case DRC_PRES_MODE_WRAP_ENCODER_TARGET:
+        mEncoderTarget = value;
+        break;
+    default:
+        break;
+    }
+    mDataUpdate = true;
+}
+
+void
+CDrcPresModeWrapper::update()
+{
+    // Get Data from Decoder
+    int progRefLevel = mStreamPRL;
+    int drcPresMode = mStreamDRCPresMode;
+
+    // by default, do as desired
+    int newTarget         = mDesTarget;
+    int newAttFactor      = mDesAttFactor;
+    int newBoostFactor    = mDesBoostFactor;
+    int newHeavy          = mDesHeavy;
+
+    if (mDataUpdate) {
+        // sanity check
+        if (mDesTarget < MAX_TARGET_LEVEL){
+            mDesTarget = MAX_TARGET_LEVEL;  // limit target level to -16 dB or below
+            newTarget = MAX_TARGET_LEVEL;
+        }
+
+        if (mEncoderTarget != -1) {
+            if (mDesTarget<124) { // if target level > -31 dB
+                if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) {
+                    // no stereo or mono downmixing, calculated scaling of light DRC
+                    /* use as little compression as possible */
+                    newAttFactor = 0;
+                    newBoostFactor = 0;
+                    if (mDesTarget<progRefLevel) { // if target level > PRL
+                        if (mEncoderTarget < mDesTarget) { // if mEncoderTarget > target level
+                            // mEncoderTarget > target level > PRL
+                            int calcFactor;
+                            float calcFactor_norm;
+                            // 0.0f < calcFactor_norm < 1.0f
+                            calcFactor_norm = (float)(mDesTarget - progRefLevel) /
+                                    (float)(mEncoderTarget - progRefLevel);
+                            calcFactor = (int)(calcFactor_norm*127.0f); // 0 <= calcFactor < 127
+                            // calcFactor is the lower limit
+                            newAttFactor = (calcFactor>newAttFactor) ? calcFactor : newAttFactor;
+                            // new AttFactor will be always = calcFactor, as it is set to 0 before.
+                            newBoostFactor = newAttFactor;
+                        } else {
+                            /* target level > mEncoderTarget > PRL */
+                            // newTDLimiterEnable = 1;
+                            // the time domain limiter must always be active in this case.
+                            //     It is assumed that the framework activates it by default
+                            newAttFactor = 127;
+                            newBoostFactor = 127;
+                        }
+                    } else { // target level <= PRL
+                        // no restrictions required
+                        // newAttFactor = newAttFactor;
+                    }
+                } else { // downmixing
+                    // if target level > -23 dB or mono downmix
+                    if ( (mDesTarget<92) || mIsMonoDownmix ) {
+                        newHeavy = 1;
+                    } else {
+                        // we perform a downmix, so, we need at least full light DRC
+                        newAttFactor = 127;
+                    }
+                }
+            } else { // target level <= -31 dB
+                // playback -31 dB: light DRC only needed if we perform downmixing
+                if (mIsDownmix) {   // we do downmixing
+                    newAttFactor = 127;
+                }
+            }
+        }
+        else { // handle other used encoder target levels
+
+            // Sanity check: DRC presentation mode is only specified for max. 5.1 channels
+            if (mStreamNrAACChan > 6) {
+                drcPresMode = 0;
+            }
+
+            switch (drcPresMode) {
+            case 0:
+            default: // presentation mode not indicated
+            {
+
+                if (mDesTarget<124) { // if target level > -31 dB
+                    // no stereo or mono downmixing
+                    if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) {
+                        if (mDesTarget<progRefLevel) { // if target level > PRL
+                            // newTDLimiterEnable = 1;
+                            // the time domain limiter must always be active in this case.
+                            //    It is assumed that the framework activates it by default
+                            newAttFactor = 127; // at least, use light compression
+                        } else { // target level <= PRL
+                            // no restrictions required
+                            // newAttFactor = newAttFactor;
+                        }
+                    } else { // downmixing
+                        // newTDLimiterEnable = 1;
+                        // the time domain limiter must always be active in this case.
+                        //    It is assumed that the framework activates it by default
+
+                        // if target level > -23 dB or mono downmix
+                        if ( (mDesTarget < 92) || mIsMonoDownmix ) {
+                            newHeavy = 1;
+                        } else{
+                            // we perform a downmix, so, we need at least full light DRC
+                            newAttFactor = 127;
+                        }
+                    }
+                } else { // target level <= -31 dB
+                    if (mIsDownmix) {   // we do downmixing.
+                        // newTDLimiterEnable = 1;
+                        // the time domain limiter must always be active in this case.
+                        //    It is assumed that the framework activates it by default
+                        newAttFactor = 127;
+                    }
+                }
+            }
+            break;
+
+            // Presentation mode 1 and 2 according to ETSI TS 101 154:
+            // Digital Video Broadcasting (DVB); Specification for the use of Video and Audio Coding
+            // in Broadcasting Applications based on the MPEG-2 Transport Stream,
+            // section C.5.4., "Decoding", and Table C.33
+            // ISO DRC            -> newHeavy = 0  (Use light compression, MPEG-style)
+            // Compression_value  -> newHeavy = 1  (Use heavy compression, DVB-style)
+            // scaling restricted -> newAttFactor = 127
+
+            case 1: // presentation mode 1, Light:-31/Heavy:-23
+            {
+                if (mDesTarget < 124) { // if target level > -31 dB
+                    // playback up to -23 dB
+                    newHeavy = 1;
+                } else { // target level <= -31 dB
+                    // playback -31 dB
+                    if (mIsDownmix) {   // we do downmixing.
+                        newAttFactor = 127;
+                    }
+                }
+            }
+            break;
+
+            case 2: // presentation mode 2, Light:-23/Heavy:-23
+            {
+                if (mDesTarget < 124) { // if target level > -31 dB
+                    // playback up to -23 dB
+                    if (mIsMonoDownmix) { // if mono downmix
+                        newHeavy = 1;
+                    } else {
+                        newHeavy = 0;
+                        newAttFactor = 127;
+                    }
+                } else { // target level <= -31 dB
+                    // playback -31 dB
+                    newHeavy = 0;
+                    if (mIsDownmix) {   // we do downmixing.
+                        newAttFactor = 127;
+                    }
+                }
+            }
+            break;
+
+            } // switch()
+        } // if (mEncoderTarget  == GPM_ENCODER_TARGET_LEVEL)
+
+        // sanity again
+        if (newHeavy == 1) {
+            newBoostFactor=127; // not really needed as the same would be done by the decoder anyway
+            newAttFactor = 127;
+        }
+
+        // update the decoder
+        if (newTarget != mLastTarget) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_REFERENCE_LEVEL, newTarget);
+            mLastTarget = newTarget;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newTarget != mDesTarget)
+                ALOGV("DRC presentation mode wrapper: forced target level to %d (from %d)\n", newTarget, mDesTarget);
+            else
+                ALOGV("DRC presentation mode wrapper: set target level to %d\n", newTarget);
+#endif
+        }
+
+        if (newAttFactor != mLastAttFactor) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_ATTENUATION_FACTOR, newAttFactor);
+            mLastAttFactor = newAttFactor;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newAttFactor != mDesAttFactor)
+                ALOGV("DRC presentation mode wrapper: forced attenuation factor to %d (from %d)\n", newAttFactor, mDesAttFactor);
+            else
+                ALOGV("DRC presentation mode wrapper: set attenuation factor to %d\n", newAttFactor);
+#endif
+        }
+
+        if (newBoostFactor != mLastBoostFactor) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_BOOST_FACTOR, newBoostFactor);
+            mLastBoostFactor = newBoostFactor;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newBoostFactor != mDesBoostFactor)
+                ALOGV("DRC presentation mode wrapper: forced boost factor to %d (from %d)\n",
+                        newBoostFactor, mDesBoostFactor);
+            else
+                ALOGV("DRC presentation mode wrapper: set boost factor to %d\n", newBoostFactor);
+#endif
+        }
+
+        if (newHeavy != mLastHeavy) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_HEAVY_COMPRESSION, newHeavy);
+            mLastHeavy = newHeavy;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newHeavy != mDesHeavy)
+                ALOGV("DRC presentation mode wrapper: forced heavy compression to %d (from %d)\n",
+                        newHeavy, mDesHeavy);
+            else
+                ALOGV("DRC presentation mode wrapper: set heavy compression to %d\n", newHeavy);
+#endif
+        }
+
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC config: tgt_lev: %3d, cut: %3d, boost: %3d, heavy: %d\n", newTarget,
+                newAttFactor, newBoostFactor, newHeavy);
+#endif
+        mDataUpdate = false;
+
+    } // if (mDataUpdate)
+}
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h
new file mode 100644
index 0000000..f0b6cf2
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 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.
+ */
+#pragma once
+#include "aacdecoder_lib.h"
+
+typedef enum
+{
+    DRC_PRES_MODE_WRAP_DESIRED_TARGET         = 0x0000,
+    DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR     = 0x0001,
+    DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR   = 0x0002,
+    DRC_PRES_MODE_WRAP_DESIRED_HEAVY          = 0x0003,
+    DRC_PRES_MODE_WRAP_ENCODER_TARGET         = 0x0004
+} DRC_PRES_MODE_WRAP_PARAM;
+
+
+class CDrcPresModeWrapper {
+public:
+    CDrcPresModeWrapper();
+    ~CDrcPresModeWrapper();
+    void setDecoderHandle(const HANDLE_AACDECODER handle);
+    void setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value);
+    void submitStreamData(CStreamInfo*);
+    void update();
+
+protected:
+    HANDLE_AACDECODER mHandleDecoder;
+    int mDesTarget;
+    int mDesAttFactor;
+    int mDesBoostFactor;
+    int mDesHeavy;
+
+    int mEncoderTarget;
+
+    int mLastTarget;
+    int mLastAttFactor;
+    int mLastBoostFactor;
+    int mLastHeavy;
+
+    SCHAR mStreamPRL;
+    SCHAR mStreamDRCPresMode;
+    INT mStreamNrAACChan;
+    INT mStreamNrOutChan;
+
+    bool mIsDownmix;
+    bool mIsMonoDownmix;
+    bool mIsStereoDownmix;
+
+    bool mDataUpdate;
+};
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 532e36f..a0e3265 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -25,16 +25,22 @@
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MediaErrors.h>
 
+#include <math.h>
+
 #define FILEREAD_MAX_LAYERS 2
 
 #define DRC_DEFAULT_MOBILE_REF_LEVEL 64  /* 64*-0.25dB = -16 dB below full scale for mobile conf */
 #define DRC_DEFAULT_MOBILE_DRC_CUT   127 /* maximum compression of dynamic range for mobile conf */
 #define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1   /* switch for heavy compression for mobile conf */
+#define DRC_DEFAULT_MOBILE_ENC_LEVEL -1 /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
 #define MAX_CHANNEL_COUNT            8  /* maximum number of audio channels that can be decoded */
 // names of properties that can be used to override the default DRC settings
 #define PROP_DRC_OVERRIDE_REF_LEVEL  "aac_drc_reference_level"
 #define PROP_DRC_OVERRIDE_CUT        "aac_drc_cut"
 #define PROP_DRC_OVERRIDE_BOOST      "aac_drc_boost"
+#define PROP_DRC_OVERRIDE_HEAVY      "aac_drc_heavy"
+#define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level"
 
 namespace android {
 
@@ -57,18 +63,19 @@
       mStreamInfo(NULL),
       mIsADTS(false),
       mInputBufferCount(0),
+      mOutputBufferCount(0),
       mSignalledError(false),
-      mSawInputEos(false),
-      mSignalledOutputEos(false),
-      mAnchorTimeUs(0),
-      mNumSamplesOutput(0),
       mOutputPortSettingsChange(NONE) {
+    for (unsigned int i = 0; i < kNumDelayBlocksMax; i++) {
+        mAnchorTimeUs[i] = 0;
+    }
     initPorts();
     CHECK_EQ(initDecoder(), (status_t)OK);
 }
 
 SoftAAC2::~SoftAAC2() {
     aacDecoder_Close(mAACDecoder);
+    delete mOutputDelayRingBuffer;
 }
 
 void SoftAAC2::initPorts() {
@@ -121,36 +128,72 @@
             status = OK;
         }
     }
-    mDecoderHasData = false;
 
-    // for streams that contain metadata, use the mobile profile DRC settings unless overridden
-    // by platform properties:
+    mEndOfInput = false;
+    mEndOfOutput = false;
+    mOutputDelayCompensated = 0;
+    mOutputDelayRingBufferSize = 2048 * MAX_CHANNEL_COUNT * kNumDelayBlocksMax;
+    mOutputDelayRingBuffer = new short[mOutputDelayRingBufferSize];
+    mOutputDelayRingBufferWritePos = 0;
+    mOutputDelayRingBufferReadPos = 0;
+
+    if (mAACDecoder == NULL) {
+        ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code");
+    }
+
+    //aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE, 0);
+
+    //init DRC wrapper
+    mDrcWrap.setDecoderHandle(mAACDecoder);
+    mDrcWrap.submitStreamData(mStreamInfo);
+
+    // for streams that contain metadata, use the mobile profile DRC settings unless overridden by platform properties
+    // TODO: change the DRC settings depending on audio output device type (HDMI, loadspeaker, headphone)
     char value[PROPERTY_VALUE_MAX];
-    //  * AAC_DRC_REFERENCE_LEVEL
+    //  DRC_PRES_MODE_WRAP_DESIRED_TARGET
     if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) {
         unsigned refLevel = atoi(value);
-        ALOGV("AAC decoder using AAC_DRC_REFERENCE_LEVEL of %d instead of %d",
-                refLevel, DRC_DEFAULT_MOBILE_REF_LEVEL);
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, refLevel);
+        ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d", refLevel,
+                DRC_DEFAULT_MOBILE_REF_LEVEL);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, refLevel);
     } else {
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, DRC_DEFAULT_MOBILE_REF_LEVEL);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, DRC_DEFAULT_MOBILE_REF_LEVEL);
     }
-    //  * AAC_DRC_ATTENUATION_FACTOR
+    //  DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR
     if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) {
         unsigned cut = atoi(value);
-        ALOGV("AAC decoder using AAC_DRC_ATTENUATION_FACTOR of %d instead of %d",
-                        cut, DRC_DEFAULT_MOBILE_DRC_CUT);
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, cut);
+        ALOGV("AAC decoder using desired DRC attenuation factor of %d instead of %d", cut,
+                DRC_DEFAULT_MOBILE_DRC_CUT);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, cut);
     } else {
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT);
     }
-    //  * AAC_DRC_BOOST_FACTOR (note: no default, using cut)
+    //  DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR
     if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) {
         unsigned boost = atoi(value);
-        ALOGV("AAC decoder using AAC_DRC_BOOST_FACTOR of %d", boost);
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, boost);
+        ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", boost,
+                DRC_DEFAULT_MOBILE_DRC_BOOST);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, boost);
     } else {
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST);
+    }
+    //  DRC_PRES_MODE_WRAP_DESIRED_HEAVY
+    if (property_get(PROP_DRC_OVERRIDE_HEAVY, value, NULL)) {
+        unsigned heavy = atoi(value);
+        ALOGV("AAC decoder using desried DRC heavy compression switch of %d instead of %d", heavy,
+                DRC_DEFAULT_MOBILE_DRC_HEAVY);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, heavy);
+    } else {
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, DRC_DEFAULT_MOBILE_DRC_HEAVY);
+    }
+    // DRC_PRES_MODE_WRAP_ENCODER_TARGET
+    if (property_get(PROP_DRC_OVERRIDE_ENC_LEVEL, value, NULL)) {
+        unsigned encoderRefLevel = atoi(value);
+        ALOGV("AAC decoder using encoder-side DRC reference level of %d instead of %d",
+                encoderRefLevel, DRC_DEFAULT_MOBILE_ENC_LEVEL);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, encoderRefLevel);
+    } else {
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, DRC_DEFAULT_MOBILE_ENC_LEVEL);
     }
 
     return status;
@@ -290,19 +333,101 @@
     return mInputBufferCount > 0;
 }
 
-void SoftAAC2::maybeConfigureDownmix() const {
-    if (mStreamInfo->numChannels > 2) {
-        char value[PROPERTY_VALUE_MAX];
-        if (!(property_get("media.aac_51_output_enabled", value, NULL) &&
-                (!strcmp(value, "1") || !strcasecmp(value, "true")))) {
-            ALOGI("Downmixing multichannel AAC to stereo");
-            aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
-            mStreamInfo->numChannels = 2;
-            // By default, the decoder creates a 5.1 channel downmix signal
-            // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output
-            // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1)
+void SoftAAC2::configureDownmix() const {
+    char value[PROPERTY_VALUE_MAX];
+    if (!(property_get("media.aac_51_output_enabled", value, NULL)
+            && (!strcmp(value, "1") || !strcasecmp(value, "true")))) {
+        ALOGI("limiting to stereo output");
+        aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
+        // By default, the decoder creates a 5.1 channel downmix signal
+        // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output
+        // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1)
+    }
+}
+
+bool SoftAAC2::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) {
+    if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize
+            && (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos
+                    || mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) {
+        // faster memcopy loop without checks, if the preconditions allow this
+        for (int32_t i = 0; i < numSamples; i++) {
+            mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos++] = samples[i];
+        }
+
+        if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
+            mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
+        }
+        if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+            ALOGE("RING BUFFER OVERFLOW");
+            return false;
+        }
+    } else {
+        ALOGV("slow SoftAAC2::outputDelayRingBufferPutSamples()");
+
+        for (int32_t i = 0; i < numSamples; i++) {
+            mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos] = samples[i];
+            mOutputDelayRingBufferWritePos++;
+            if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
+                mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
+            }
+            if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+                ALOGE("RING BUFFER OVERFLOW");
+                return false;
+            }
         }
     }
+    return true;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) {
+    if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize
+            && (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos
+                    || mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) {
+        // faster memcopy loop without checks, if the preconditions allow this
+        if (samples != 0) {
+            for (int32_t i = 0; i < numSamples; i++) {
+                samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos++];
+            }
+        } else {
+            mOutputDelayRingBufferReadPos += numSamples;
+        }
+        if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
+            mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
+        }
+    } else {
+        ALOGV("slow SoftAAC2::outputDelayRingBufferGetSamples()");
+
+        for (int32_t i = 0; i < numSamples; i++) {
+            if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+                ALOGE("RING BUFFER UNDERRUN");
+                return -1;
+            }
+            if (samples != 0) {
+                samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos];
+            }
+            mOutputDelayRingBufferReadPos++;
+            if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
+                mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
+            }
+        }
+    }
+    return numSamples;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferSamplesAvailable() {
+    int32_t available = mOutputDelayRingBufferWritePos - mOutputDelayRingBufferReadPos;
+    if (available < 0) {
+        available += mOutputDelayRingBufferSize;
+    }
+    if (available < 0) {
+        ALOGE("FATAL RING BUFFER ERROR");
+        return 0;
+    }
+    return available;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferSamplesLeft() {
+    return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
 }
 
 void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
@@ -318,12 +443,11 @@
     List<BufferInfo *> &outQueue = getPortQueue(1);
 
     if (portIndex == 0 && mInputBufferCount == 0) {
-        ++mInputBufferCount;
-        BufferInfo *info = *inQueue.begin();
-        OMX_BUFFERHEADERTYPE *header = info->mHeader;
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
 
-        inBuffer[0] = header->pBuffer + header->nOffset;
-        inBufferLength[0] = header->nFilledLen;
+        inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+        inBufferLength[0] = inHeader->nFilledLen;
 
         AAC_DECODER_ERROR decoderErr =
             aacDecoder_ConfigRaw(mAACDecoder,
@@ -331,19 +455,25 @@
                                  inBufferLength);
 
         if (decoderErr != AAC_DEC_OK) {
+            ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
             mSignalledError = true;
             notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
             return;
         }
 
-        inQueue.erase(inQueue.begin());
-        info->mOwnedByUs = false;
-        notifyEmptyBufferDone(header);
+        mInputBufferCount++;
+        mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned
 
+        inInfo->mOwnedByUs = false;
+        inQueue.erase(inQueue.begin());
+        inInfo = NULL;
+        notifyEmptyBufferDone(inHeader);
+        inHeader = NULL;
+
+        configureDownmix();
         // Only send out port settings changed event if both sample rate
         // and numChannels are valid.
         if (mStreamInfo->sampleRate && mStreamInfo->numChannels) {
-            maybeConfigureDownmix();
             ALOGI("Initially configuring decoder: %d Hz, %d channels",
                 mStreamInfo->sampleRate,
                 mStreamInfo->numChannels);
@@ -355,146 +485,20 @@
         return;
     }
 
-    while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) {
-        BufferInfo *inInfo = NULL;
-        OMX_BUFFERHEADERTYPE *inHeader = NULL;
+    while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) {
         if (!inQueue.empty()) {
-            inInfo = *inQueue.begin();
-            inHeader = inInfo->mHeader;
-        }
+            INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+            BufferInfo *inInfo = *inQueue.begin();
+            OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
 
-        BufferInfo *outInfo = *outQueue.begin();
-        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
-        outHeader->nFlags = 0;
-
-        if (inHeader) {
             if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
-                mSawInputEos = true;
-            }
-
-            if (inHeader->nOffset == 0 && inHeader->nFilledLen) {
-                mAnchorTimeUs = inHeader->nTimeStamp;
-                mNumSamplesOutput = 0;
-            }
-
-            if (mIsADTS && inHeader->nFilledLen) {
-                size_t adtsHeaderSize = 0;
-                // skip 30 bits, aac_frame_length follows.
-                // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
-
-                const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
-
-                bool signalError = false;
-                if (inHeader->nFilledLen < 7) {
-                    ALOGE("Audio data too short to contain even the ADTS header. "
-                          "Got %d bytes.", inHeader->nFilledLen);
-                    hexdump(adtsHeader, inHeader->nFilledLen);
-                    signalError = true;
-                } else {
-                    bool protectionAbsent = (adtsHeader[1] & 1);
-
-                    unsigned aac_frame_length =
-                        ((adtsHeader[3] & 3) << 11)
-                        | (adtsHeader[4] << 3)
-                        | (adtsHeader[5] >> 5);
-
-                    if (inHeader->nFilledLen < aac_frame_length) {
-                        ALOGE("Not enough audio data for the complete frame. "
-                              "Got %d bytes, frame size according to the ADTS "
-                              "header is %u bytes.",
-                              inHeader->nFilledLen, aac_frame_length);
-                        hexdump(adtsHeader, inHeader->nFilledLen);
-                        signalError = true;
-                    } else {
-                        adtsHeaderSize = (protectionAbsent ? 7 : 9);
-
-                        inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
-                        inBufferLength[0] = aac_frame_length - adtsHeaderSize;
-
-                        inHeader->nOffset += adtsHeaderSize;
-                        inHeader->nFilledLen -= adtsHeaderSize;
-                    }
-                }
-
-                if (signalError) {
-                    mSignalledError = true;
-
-                    notify(OMX_EventError,
-                           OMX_ErrorStreamCorrupt,
-                           ERROR_MALFORMED,
-                           NULL);
-
-                    return;
-                }
+                mEndOfInput = true;
             } else {
-                inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
-                inBufferLength[0] = inHeader->nFilledLen;
+                mEndOfInput = false;
             }
-        } else {
-            inBufferLength[0] = 0;
-        }
-
-        // Fill and decode
-        INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(
-                outHeader->pBuffer + outHeader->nOffset);
-
-        bytesValid[0] = inBufferLength[0];
-
-        int prevSampleRate = mStreamInfo->sampleRate;
-        int prevNumChannels = mStreamInfo->numChannels;
-
-        AAC_DECODER_ERROR decoderErr = AAC_DEC_NOT_ENOUGH_BITS;
-        while ((bytesValid[0] > 0 || mSawInputEos) && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
-            mDecoderHasData |= (bytesValid[0] > 0);
-            aacDecoder_Fill(mAACDecoder,
-                            inBuffer,
-                            inBufferLength,
-                            bytesValid);
-
-            decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
-                                                outBuffer,
-                                                outHeader->nAllocLen,
-                                                0 /* flags */);
-            if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
-                if (mSawInputEos && bytesValid[0] <= 0) {
-                    if (mDecoderHasData) {
-                        // flush out the decoder's delayed data by calling DecodeFrame
-                        // one more time, with the AACDEC_FLUSH flag set
-                        decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
-                                                            outBuffer,
-                                                            outHeader->nAllocLen,
-                                                            AACDEC_FLUSH);
-                        mDecoderHasData = false;
-                    }
-                    outHeader->nFlags = OMX_BUFFERFLAG_EOS;
-                    mSignalledOutputEos = true;
-                    break;
-                } else {
-                    ALOGW("Not enough bits, bytesValid %d", bytesValid[0]);
-                }
-            }
-        }
-
-        size_t numOutBytes =
-            mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
-
-        if (inHeader) {
-            if (decoderErr == AAC_DEC_OK) {
-                UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
-                inHeader->nFilledLen -= inBufferUsedLength;
-                inHeader->nOffset += inBufferUsedLength;
-            } else {
-                ALOGW("AAC decoder returned error %d, substituting silence",
-                      decoderErr);
-
-                memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes);
-
-                // Discard input buffer.
-                inHeader->nFilledLen = 0;
-
-                aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
-
-                // fall through
+            if (inHeader->nOffset == 0) { // TODO: does nOffset != 0 happen?
+                mAnchorTimeUs[mInputBufferCount % kNumDelayBlocksMax] =
+                        inHeader->nTimeStamp;
             }
 
             if (inHeader->nFilledLen == 0) {
@@ -503,54 +507,282 @@
                 inInfo = NULL;
                 notifyEmptyBufferDone(inHeader);
                 inHeader = NULL;
+            } else {
+                if (mIsADTS) {
+                    size_t adtsHeaderSize = 0;
+                    // skip 30 bits, aac_frame_length follows.
+                    // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
+
+                    const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
+
+                    bool signalError = false;
+                    if (inHeader->nFilledLen < 7) {
+                        ALOGE("Audio data too short to contain even the ADTS header. "
+                                "Got %d bytes.", inHeader->nFilledLen);
+                        hexdump(adtsHeader, inHeader->nFilledLen);
+                        signalError = true;
+                    } else {
+                        bool protectionAbsent = (adtsHeader[1] & 1);
+
+                        unsigned aac_frame_length =
+                            ((adtsHeader[3] & 3) << 11)
+                            | (adtsHeader[4] << 3)
+                            | (adtsHeader[5] >> 5);
+
+                        if (inHeader->nFilledLen < aac_frame_length) {
+                            ALOGE("Not enough audio data for the complete frame. "
+                                    "Got %d bytes, frame size according to the ADTS "
+                                    "header is %u bytes.",
+                                    inHeader->nFilledLen, aac_frame_length);
+                            hexdump(adtsHeader, inHeader->nFilledLen);
+                            signalError = true;
+                        } else {
+                            adtsHeaderSize = (protectionAbsent ? 7 : 9);
+
+                            inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
+                            inBufferLength[0] = aac_frame_length - adtsHeaderSize;
+
+                            inHeader->nOffset += adtsHeaderSize;
+                            inHeader->nFilledLen -= adtsHeaderSize;
+                        }
+                    }
+
+                    if (signalError) {
+                        mSignalledError = true;
+
+                        notify(OMX_EventError,
+                               OMX_ErrorStreamCorrupt,
+                               ERROR_MALFORMED,
+                               NULL);
+
+                        return;
+                    }
+                } else {
+                    inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+                    inBufferLength[0] = inHeader->nFilledLen;
+                }
+
+                // Fill and decode
+                bytesValid[0] = inBufferLength[0];
+
+                INT prevSampleRate = mStreamInfo->sampleRate;
+                INT prevNumChannels = mStreamInfo->numChannels;
+
+                aacDecoder_Fill(mAACDecoder,
+                                inBuffer,
+                                inBufferLength,
+                                bytesValid);
+
+                 // run DRC check
+                 mDrcWrap.submitStreamData(mStreamInfo);
+                 mDrcWrap.update();
+
+                AAC_DECODER_ERROR decoderErr =
+                    aacDecoder_DecodeFrame(mAACDecoder,
+                                           tmpOutBuffer,
+                                           2048 * MAX_CHANNEL_COUNT,
+                                           0 /* flags */);
+
+                if (decoderErr != AAC_DEC_OK) {
+                    ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+                }
+
+                if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
+                    ALOGE("AAC_DEC_NOT_ENOUGH_BITS should never happen");
+                    mSignalledError = true;
+                    notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                    return;
+                }
+
+                if (bytesValid[0] != 0) {
+                    ALOGE("bytesValid[0] != 0 should never happen");
+                    mSignalledError = true;
+                    notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                    return;
+                }
+
+                size_t numOutBytes =
+                    mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
+
+                if (decoderErr == AAC_DEC_OK) {
+                    if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+                            mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+                        mSignalledError = true;
+                        notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                        return;
+                    }
+                    UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
+                    inHeader->nFilledLen -= inBufferUsedLength;
+                    inHeader->nOffset += inBufferUsedLength;
+                } else {
+                    ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
+
+                    memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
+
+                    if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+                            mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+                        mSignalledError = true;
+                        notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                        return;
+                    }
+
+                    // Discard input buffer.
+                    inHeader->nFilledLen = 0;
+
+                    aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
+
+                    // fall through
+                }
+
+                /*
+                 * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+                 * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+                 * rate system and the sampling rate in the final output is actually
+                 * doubled compared with the core AAC decoder sampling rate.
+                 *
+                 * Explicit signalling is done by explicitly defining SBR audio object
+                 * type in the bitstream. Implicit signalling is done by embedding
+                 * SBR content in AAC extension payload specific to SBR, and hence
+                 * requires an AAC decoder to perform pre-checks on actual audio frames.
+                 *
+                 * Thus, we could not say for sure whether a stream is
+                 * AAC+/eAAC+ until the first data frame is decoded.
+                 */
+                if (mOutputBufferCount > 1) {
+                    if (mStreamInfo->sampleRate != prevSampleRate ||
+                        mStreamInfo->numChannels != prevNumChannels) {
+                        ALOGE("can not reconfigure AAC output");
+                        mSignalledError = true;
+                        notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                        return;
+                    }
+                }
+                if (mInputBufferCount <= 2) { // TODO: <= 1
+                    if (mStreamInfo->sampleRate != prevSampleRate ||
+                        mStreamInfo->numChannels != prevNumChannels) {
+                        ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+                              prevSampleRate, mStreamInfo->sampleRate,
+                              prevNumChannels, mStreamInfo->numChannels);
+
+                        notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+                        mOutputPortSettingsChange = AWAITING_DISABLED;
+
+                        if (inHeader->nFilledLen == 0) {
+                            inInfo->mOwnedByUs = false;
+                            mInputBufferCount++;
+                            inQueue.erase(inQueue.begin());
+                            inInfo = NULL;
+                            notifyEmptyBufferDone(inHeader);
+                            inHeader = NULL;
+                        }
+                        return;
+                    }
+                } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
+                    ALOGW("Invalid AAC stream");
+                    mSignalledError = true;
+                    notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                    return;
+                }
+                if (inHeader->nFilledLen == 0) {
+                    inInfo->mOwnedByUs = false;
+                    mInputBufferCount++;
+                    inQueue.erase(inQueue.begin());
+                    inInfo = NULL;
+                    notifyEmptyBufferDone(inHeader);
+                    inHeader = NULL;
+                } else {
+                    ALOGW("inHeader->nFilledLen = %d", inHeader->nFilledLen);
+                }
             }
         }
 
-        /*
-         * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
-         * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
-         * rate system and the sampling rate in the final output is actually
-         * doubled compared with the core AAC decoder sampling rate.
-         *
-         * Explicit signalling is done by explicitly defining SBR audio object
-         * type in the bitstream. Implicit signalling is done by embedding
-         * SBR content in AAC extension payload specific to SBR, and hence
-         * requires an AAC decoder to perform pre-checks on actual audio frames.
-         *
-         * Thus, we could not say for sure whether a stream is
-         * AAC+/eAAC+ until the first data frame is decoded.
-         */
-        if (mInputBufferCount <= 2) {
-            if (mStreamInfo->sampleRate != prevSampleRate ||
-                mStreamInfo->numChannels != prevNumChannels) {
-                maybeConfigureDownmix();
-                ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
-                      prevSampleRate, mStreamInfo->sampleRate,
-                      prevNumChannels, mStreamInfo->numChannels);
+        int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
 
-                notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
-                mOutputPortSettingsChange = AWAITING_DISABLED;
+        if (!mEndOfInput && mOutputDelayCompensated < outputDelay) {
+            // discard outputDelay at the beginning
+            int32_t toCompensate = outputDelay - mOutputDelayCompensated;
+            int32_t discard = outputDelayRingBufferSamplesAvailable();
+            if (discard > toCompensate) {
+                discard = toCompensate;
+            }
+            int32_t discarded = outputDelayRingBufferGetSamples(0, discard);
+            mOutputDelayCompensated += discarded;
+            continue;
+        }
+
+        if (mEndOfInput) {
+            while (mOutputDelayCompensated > 0) {
+                // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
+                INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+ 
+                 // run DRC check
+                 mDrcWrap.submitStreamData(mStreamInfo);
+                 mDrcWrap.update();
+
+                AAC_DECODER_ERROR decoderErr =
+                    aacDecoder_DecodeFrame(mAACDecoder,
+                                           tmpOutBuffer,
+                                           2048 * MAX_CHANNEL_COUNT,
+                                           AACDEC_FLUSH);
+                if (decoderErr != AAC_DEC_OK) {
+                    ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+                }
+
+                int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
+                if (tmpOutBufferSamples > mOutputDelayCompensated) {
+                    tmpOutBufferSamples = mOutputDelayCompensated;
+                }
+                outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
+                mOutputDelayCompensated -= tmpOutBufferSamples;
+            }
+        }
+
+        while (!outQueue.empty()
+                && outputDelayRingBufferSamplesAvailable()
+                        >= mStreamInfo->frameSize * mStreamInfo->numChannels) {
+            BufferInfo *outInfo = *outQueue.begin();
+            OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+            if (outHeader->nOffset != 0) {
+                ALOGE("outHeader->nOffset != 0 is not handled");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
                 return;
             }
-        } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
-            ALOGW("Invalid AAC stream");
-            mSignalledError = true;
-            notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
-            return;
-        }
 
-        if (decoderErr == AAC_DEC_OK || mNumSamplesOutput > 0) {
-            // We'll only output data if we successfully decoded it or
-            // we've previously decoded valid data, in the latter case
-            // (decode failed) we'll output a silent frame.
-            outHeader->nFilledLen = numOutBytes;
+            INT_PCM *outBuffer =
+                    reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
+            if (outHeader->nOffset
+                    + mStreamInfo->frameSize * mStreamInfo->numChannels * sizeof(int16_t)
+                    > outHeader->nAllocLen) {
+                ALOGE("buffer overflow");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                return;
 
-            outHeader->nTimeStamp =
-                mAnchorTimeUs
-                    + (mNumSamplesOutput * 1000000ll) / mStreamInfo->sampleRate;
+            }
+            int32_t ns = outputDelayRingBufferGetSamples(outBuffer,
+                    mStreamInfo->frameSize * mStreamInfo->numChannels); // TODO: check for overflow
+            if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) {
+                ALOGE("not a complete frame of samples available");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                return;
+            }
 
-            mNumSamplesOutput += mStreamInfo->frameSize;
+            outHeader->nFilledLen = mStreamInfo->frameSize * mStreamInfo->numChannels
+                    * sizeof(int16_t);
+            if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
+                outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+                mEndOfOutput = true;
+            } else {
+                outHeader->nFlags = 0;
+            }
 
+            outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount
+                    % kNumDelayBlocksMax];
+
+            mOutputBufferCount++;
             outInfo->mOwnedByUs = false;
             outQueue.erase(outQueue.begin());
             outInfo = NULL;
@@ -558,8 +790,48 @@
             outHeader = NULL;
         }
 
-        if (decoderErr == AAC_DEC_OK) {
-            ++mInputBufferCount;
+        if (mEndOfInput) {
+            if (outputDelayRingBufferSamplesAvailable() > 0
+                    && outputDelayRingBufferSamplesAvailable()
+                            < mStreamInfo->frameSize * mStreamInfo->numChannels) {
+                ALOGE("not a complete frame of samples available");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                return;
+            }
+
+            if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
+                if (!mEndOfOutput) {
+                    // send empty block signaling EOS
+                    mEndOfOutput = true;
+                    BufferInfo *outInfo = *outQueue.begin();
+                    OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+                    if (outHeader->nOffset != 0) {
+                        ALOGE("outHeader->nOffset != 0 is not handled");
+                        mSignalledError = true;
+                        notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                        return;
+                    }
+
+                    INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(outHeader->pBuffer
+                            + outHeader->nOffset);
+                    int32_t ns = 0;
+                    outHeader->nFilledLen = 0;
+                    outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+                    outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount
+                            % kNumDelayBlocksMax];
+
+                    mOutputBufferCount++;
+                    outInfo->mOwnedByUs = false;
+                    outQueue.erase(outQueue.begin());
+                    outInfo = NULL;
+                    notifyFillBufferDone(outHeader);
+                    outHeader = NULL;
+                }
+                break; // if outQueue not empty but no more output
+            }
         }
     }
 }
@@ -574,34 +846,67 @@
         // but only if initialization has already happened.
         if (mInputBufferCount != 0) {
             mInputBufferCount = 1;
-            mStreamInfo->sampleRate = 0;
         }
+    } else {
+        while (outputDelayRingBufferSamplesAvailable() > 0) {
+            int32_t ns = outputDelayRingBufferGetSamples(0,
+                    mStreamInfo->frameSize * mStreamInfo->numChannels);
+            if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) {
+                ALOGE("not a complete frame of samples available");
+            }
+            mOutputBufferCount++;
+        }
+        mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos;
     }
 }
 
 void SoftAAC2::drainDecoder() {
-    // a buffer big enough for 6 channels of decoded HE-AAC
-    short buf [2048*6];
-    aacDecoder_DecodeFrame(mAACDecoder,
-            buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
-    aacDecoder_DecodeFrame(mAACDecoder,
-            buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
-    aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
-    mDecoderHasData = false;
+    int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
+
+    // flush decoder until outputDelay is compensated
+    while (mOutputDelayCompensated > 0) {
+        // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
+        INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+
+        // run DRC check
+        mDrcWrap.submitStreamData(mStreamInfo);
+        mDrcWrap.update();
+
+        AAC_DECODER_ERROR decoderErr =
+            aacDecoder_DecodeFrame(mAACDecoder,
+                                   tmpOutBuffer,
+                                   2048 * MAX_CHANNEL_COUNT,
+                                   AACDEC_FLUSH);
+        if (decoderErr != AAC_DEC_OK) {
+            ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+        }
+
+        int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
+        if (tmpOutBufferSamples > mOutputDelayCompensated) {
+            tmpOutBufferSamples = mOutputDelayCompensated;
+        }
+        outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
+
+        mOutputDelayCompensated -= tmpOutBufferSamples;
+    }
 }
 
 void SoftAAC2::onReset() {
     drainDecoder();
     // reset the "configured" state
     mInputBufferCount = 0;
-    mNumSamplesOutput = 0;
+    mOutputBufferCount = 0;
+    mOutputDelayCompensated = 0;
+    mOutputDelayRingBufferWritePos = 0;
+    mOutputDelayRingBufferReadPos = 0;
+    mEndOfInput = false;
+    mEndOfOutput = false;
+
     // To make the codec behave the same before and after a reset, we need to invalidate the
     // streaminfo struct. This does that:
-    mStreamInfo->sampleRate = 0;
+    mStreamInfo->sampleRate = 0; // TODO: mStreamInfo is read only
 
     mSignalledError = false;
-    mSawInputEos = false;
-    mSignalledOutputEos = false;
     mOutputPortSettingsChange = NONE;
 }
 
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index a7ea1e2..5cde03a 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -20,6 +20,7 @@
 #include "SimpleSoftOMXComponent.h"
 
 #include "aacdecoder_lib.h"
+#include "DrcPresModeWrap.h"
 
 namespace android {
 
@@ -47,18 +48,19 @@
     enum {
         kNumInputBuffers        = 4,
         kNumOutputBuffers       = 4,
+        kNumDelayBlocksMax      = 8,
     };
 
     HANDLE_AACDECODER mAACDecoder;
     CStreamInfo *mStreamInfo;
     bool mIsADTS;
-    bool mDecoderHasData;
+    bool mIsFirst;
     size_t mInputBufferCount;
+    size_t mOutputBufferCount;
     bool mSignalledError;
-    bool mSawInputEos;
-    bool mSignalledOutputEos;
-    int64_t mAnchorTimeUs;
-    int64_t mNumSamplesOutput;
+    int64_t mAnchorTimeUs[kNumDelayBlocksMax];
+
+    CDrcPresModeWrapper mDrcWrap;
 
     enum {
         NONE,
@@ -69,9 +71,22 @@
     void initPorts();
     status_t initDecoder();
     bool isConfigured() const;
-    void maybeConfigureDownmix() const;
+    void configureDownmix() const;
     void drainDecoder();
 
+//      delay compensation
+    bool mEndOfInput;
+    bool mEndOfOutput;
+    int32_t mOutputDelayCompensated;
+    int32_t mOutputDelayRingBufferSize;
+    short *mOutputDelayRingBuffer;
+    int32_t mOutputDelayRingBufferWritePos;
+    int32_t mOutputDelayRingBufferReadPos;
+    bool outputDelayRingBufferPutSamples(INT_PCM *samples, int numSamples);
+    int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples);
+    int32_t outputDelayRingBufferSamplesAvailable();
+    int32_t outputDelayRingBufferSamplesLeft();
+
     DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2);
 };
 
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/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 31a5077..cd51bbf 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -134,10 +134,10 @@
             OMX_IN OMX_PTR pEventData);
 
     OMX_ERRORTYPE OnEmptyBufferDone(
-            node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+            node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
 
     OMX_ERRORTYPE OnFillBufferDone(
-            node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+            node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
 
     void invalidateNodeID(node_id node);
 
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index 339179e..3967dc6 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -138,12 +138,25 @@
         OMX::buffer_id mID;
     };
     Vector<ActiveBuffer> mActiveBuffers;
+#ifdef __LP64__
+    Mutex mBufferIDLock;
+    uint32_t mBufferIDCount;
+    KeyedVector<OMX::buffer_id, OMX_BUFFERHEADERTYPE *> mBufferIDToBufferHeader;
+    KeyedVector<OMX_BUFFERHEADERTYPE *, OMX::buffer_id> mBufferHeaderToBufferID;
+#endif
 
     ~OMXNodeInstance();
 
     void addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id);
     void removeActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id);
     void freeActiveBuffers();
+
+    // For buffer id management
+    OMX::buffer_id makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
+    OMX_BUFFERHEADERTYPE *findBufferHeader(OMX::buffer_id buffer);
+    OMX::buffer_id findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
+    void invalidateBufferID(OMX::buffer_id buffer);
+
     status_t useGraphicBuffer2_l(
             OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
             OMX::buffer_id *buffer);
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index b62d5f5..22b12d9 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -287,6 +287,7 @@
 status_t OMX::getParameter(
         node_id node, OMX_INDEXTYPE index,
         void *params, size_t size) {
+    ALOGV("getParameter(%u %#x %p %zd)", node, index, params, size);
     return findInstance(node)->getParameter(
             index, params, size);
 }
@@ -294,6 +295,7 @@
 status_t OMX::setParameter(
         node_id node, OMX_INDEXTYPE index,
         const void *params, size_t size) {
+    ALOGV("setParameter(%u %#x %p %zd)", node, index, params, size);
     return findInstance(node)->setParameter(
             index, params, size);
 }
@@ -445,13 +447,13 @@
 }
 
 OMX_ERRORTYPE OMX::OnEmptyBufferDone(
-        node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+        node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
     ALOGV("OnEmptyBufferDone buffer=%p", pBuffer);
 
     omx_message msg;
     msg.type = omx_message::EMPTY_BUFFER_DONE;
     msg.node = node;
-    msg.u.buffer_data.buffer = pBuffer;
+    msg.u.buffer_data.buffer = buffer;
 
     findDispatcher(node)->post(msg);
 
@@ -459,13 +461,13 @@
 }
 
 OMX_ERRORTYPE OMX::OnFillBufferDone(
-        node_id node, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+        node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
     ALOGV("OnFillBufferDone buffer=%p", pBuffer);
 
     omx_message msg;
     msg.type = omx_message::FILL_BUFFER_DONE;
     msg.node = node;
-    msg.u.extended_buffer_data.buffer = pBuffer;
+    msg.u.extended_buffer_data.buffer = buffer;
     msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
     msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
     msg.u.extended_buffer_data.flags = pBuffer->nFlags;
@@ -479,7 +481,7 @@
 OMX::node_id OMX::makeNodeID(OMXNodeInstance *instance) {
     // mLock is already held.
 
-    node_id node = (node_id)(uintptr_t)++mNodeCounter;
+    node_id node = (node_id)++mNodeCounter;
     mNodeIDToInstance.add(node, instance);
 
     return node;
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 0fb38fa..d6ab109 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -92,10 +92,14 @@
 OMXNodeInstance::OMXNodeInstance(
         OMX *owner, const sp<IOMXObserver> &observer)
     : mOwner(owner),
-      mNodeID(NULL),
+      mNodeID(0),
       mHandle(NULL),
       mObserver(observer),
-      mDying(false) {
+      mDying(false)
+#ifdef __LP64__
+      , mBufferIDCount(0)
+#endif
+{
 }
 
 OMXNodeInstance::~OMXNodeInstance() {
@@ -232,7 +236,7 @@
     }
 
     mOwner->invalidateNodeID(mNodeID);
-    mNodeID = NULL;
+    mNodeID = 0;
 
     ALOGV("OMXNodeInstance going away.");
     delete this;
@@ -270,7 +274,7 @@
     Mutex::Autolock autoLock(mLock);
 
     OMX_ERRORTYPE err = OMX_GetParameter(mHandle, index, params);
-
+    ALOGE_IF(err != OMX_ErrorNone, "getParameter(%d) ERROR: %#x", index, err);
     return StatusFromOMXError(err);
 }
 
@@ -280,7 +284,7 @@
 
     OMX_ERRORTYPE err = OMX_SetParameter(
             mHandle, index, const_cast<void *>(params));
-
+    ALOGE_IF(err != OMX_ErrorNone, "setParameter(%d) ERROR: %#x", index, err);
     return StatusFromOMXError(err);
 }
 
@@ -482,7 +486,7 @@
 
     CHECK_EQ(header->pAppPrivate, buffer_meta);
 
-    *buffer = header;
+    *buffer = makeBufferID(header);
 
     addActiveBuffer(portIndex, *buffer);
 
@@ -538,7 +542,7 @@
     CHECK_EQ(header->pBuffer, bufferHandle);
     CHECK_EQ(header->pAppPrivate, bufferMeta);
 
-    *buffer = header;
+    *buffer = makeBufferID(header);
 
     addActiveBuffer(portIndex, *buffer);
 
@@ -602,7 +606,7 @@
 
     CHECK_EQ(header->pAppPrivate, bufferMeta);
 
-    *buffer = header;
+    *buffer = makeBufferID(header);
 
     addActiveBuffer(portIndex, *buffer);
 
@@ -614,7 +618,7 @@
         OMX::buffer_id buffer) {
     Mutex::Autolock autoLock(mLock);
 
-    OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)(buffer);
+    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer);
     VideoDecoderOutputMetaData *metadata =
         (VideoDecoderOutputMetaData *)(header->pBuffer);
     BufferMeta *bufferMeta = (BufferMeta *)(header->pAppPrivate);
@@ -710,7 +714,7 @@
 
     CHECK_EQ(header->pAppPrivate, buffer_meta);
 
-    *buffer = header;
+    *buffer = makeBufferID(header);
     *buffer_data = header->pBuffer;
 
     addActiveBuffer(portIndex, *buffer);
@@ -748,7 +752,7 @@
 
     CHECK_EQ(header->pAppPrivate, buffer_meta);
 
-    *buffer = header;
+    *buffer = makeBufferID(header);
 
     addActiveBuffer(portIndex, *buffer);
 
@@ -766,13 +770,14 @@
 
     removeActiveBuffer(portIndex, buffer);
 
-    OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer);
     BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate);
 
     OMX_ERRORTYPE err = OMX_FreeBuffer(mHandle, portIndex, header);
 
     delete buffer_meta;
     buffer_meta = NULL;
+    invalidateBufferID(buffer);
 
     return StatusFromOMXError(err);
 }
@@ -780,7 +785,7 @@
 status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) {
     Mutex::Autolock autoLock(mLock);
 
-    OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer);
     header->nFilledLen = 0;
     header->nOffset = 0;
     header->nFlags = 0;
@@ -796,7 +801,7 @@
         OMX_U32 flags, OMX_TICKS timestamp) {
     Mutex::Autolock autoLock(mLock);
 
-    OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer);
     header->nFilledLen = rangeLength;
     header->nOffset = rangeOffset;
     header->nFlags = flags;
@@ -914,8 +919,7 @@
 
     if (msg.type == omx_message::FILL_BUFFER_DONE) {
         OMX_BUFFERHEADERTYPE *buffer =
-            static_cast<OMX_BUFFERHEADERTYPE *>(
-                    msg.u.extended_buffer_data.buffer);
+            findBufferHeader(msg.u.extended_buffer_data.buffer);
 
         BufferMeta *buffer_meta =
             static_cast<BufferMeta *>(buffer->pAppPrivate);
@@ -940,8 +944,7 @@
             // be very confused.
 
             OMX_BUFFERHEADERTYPE *buffer =
-                static_cast<OMX_BUFFERHEADERTYPE *>(
-                        msg.u.buffer_data.buffer);
+                findBufferHeader(msg.u.buffer_data.buffer);
 
             bufferSource->codecBufferEmptied(buffer);
             return;
@@ -1001,7 +1004,8 @@
     if (instance->mDying) {
         return OMX_ErrorNone;
     }
-    return instance->owner()->OnEmptyBufferDone(instance->nodeID(), pBuffer);
+    return instance->owner()->OnEmptyBufferDone(instance->nodeID(),
+            instance->findBufferID(pBuffer), pBuffer);
 }
 
 // static
@@ -1013,7 +1017,8 @@
     if (instance->mDying) {
         return OMX_ErrorNone;
     }
-    return instance->owner()->OnFillBufferDone(instance->nodeID(), pBuffer);
+    return instance->owner()->OnFillBufferDone(instance->nodeID(),
+            instance->findBufferID(pBuffer), pBuffer);
 }
 
 void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id) {
@@ -1048,4 +1053,67 @@
     }
 }
 
+#ifdef __LP64__
+
+OMX::buffer_id OMXNodeInstance::makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
+    if (bufferHeader == NULL) {
+        return 0;
+    }
+    Mutex::Autolock autoLock(mBufferIDLock);
+    OMX::buffer_id buffer;
+    do { // handle the very unlikely case of ID overflow
+        if (++mBufferIDCount == 0) {
+           ++mBufferIDCount;
+        }
+        buffer = (OMX::buffer_id)mBufferIDCount;
+    } while (mBufferIDToBufferHeader.indexOfKey(buffer) >= 0);
+    mBufferIDToBufferHeader.add(buffer, bufferHeader);
+    mBufferHeaderToBufferID.add(bufferHeader, buffer);
+    return buffer;
+}
+
+OMX_BUFFERHEADERTYPE *OMXNodeInstance::findBufferHeader(OMX::buffer_id buffer) {
+    if (buffer == 0) {
+        return NULL;
+    }
+    Mutex::Autolock autoLock(mBufferIDLock);
+    return mBufferIDToBufferHeader.valueFor(buffer);
+}
+
+OMX::buffer_id OMXNodeInstance::findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
+    if (bufferHeader == NULL) {
+        return 0;
+    }
+    Mutex::Autolock autoLock(mBufferIDLock);
+    return mBufferHeaderToBufferID.valueFor(bufferHeader);
+}
+
+void OMXNodeInstance::invalidateBufferID(OMX::buffer_id buffer) {
+    if (buffer == 0) {
+        return;
+    }
+    Mutex::Autolock autoLock(mBufferIDLock);
+    mBufferHeaderToBufferID.removeItem(mBufferIDToBufferHeader.valueFor(buffer));
+    mBufferIDToBufferHeader.removeItem(buffer);
+}
+
+#else
+
+OMX::buffer_id OMXNodeInstance::makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
+    return (OMX::buffer_id)bufferHeader;
+}
+
+OMX_BUFFERHEADERTYPE *OMXNodeInstance::findBufferHeader(OMX::buffer_id buffer) {
+    return (OMX_BUFFERHEADERTYPE *)buffer;
+}
+
+OMX::buffer_id OMXNodeInstance::findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
+    return (OMX::buffer_id)bufferHeader;
+}
+
+void OMXNodeInstance::invalidateBufferID(OMX::buffer_id buffer __unused) {
+}
+
+#endif
+
 }  // namespace android
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 a7c06d5..c5d8858 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaCodec"
 
 #include "NdkMediaCodec.h"
@@ -36,14 +36,14 @@
 using namespace android;
 
 
-static int translate_error(status_t err) {
+static media_status_t translate_error(status_t err) {
     if (err == OK) {
-        return OK;
+        return AMEDIA_OK;
     } else if (err == -EAGAIN) {
-        return AMEDIACODEC_INFO_TRY_AGAIN_LATER;
+        return (media_status_t) AMEDIACODEC_INFO_TRY_AGAIN_LATER;
     }
     ALOGE("sf error code: %d", err);
-    return AMEDIAERROR_GENERIC;
+    return AMEDIA_ERROR_UNKNOWN;
 }
 
 enum {
@@ -61,6 +61,8 @@
     virtual void onMessageReceived(const sp<AMessage> &msg);
 };
 
+typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
+
 struct AMediaCodec {
     sp<android::MediaCodec> mCodec;
     sp<ALooper> mLooper;
@@ -175,7 +177,7 @@
 }
 
 EXPORT
-int AMediaCodec_delete(AMediaCodec *mData) {
+media_status_t AMediaCodec_delete(AMediaCodec *mData) {
     if (mData->mCodec != NULL) {
         mData->mCodec->release();
         mData->mCodec.clear();
@@ -187,11 +189,11 @@
         mData->mLooper.clear();
     }
     delete mData;
-    return OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-int AMediaCodec_configure(
+media_status_t AMediaCodec_configure(
         AMediaCodec *mData,
         const AMediaFormat* format,
         ANativeWindow* window,
@@ -210,7 +212,7 @@
 }
 
 EXPORT
-int AMediaCodec_start(AMediaCodec *mData) {
+media_status_t AMediaCodec_start(AMediaCodec *mData) {
     status_t ret =  mData->mCodec->start();
     if (ret != OK) {
         return translate_error(ret);
@@ -218,12 +220,12 @@
     mData->mActivityNotification = new AMessage(kWhatActivityNotify, mData->mHandler->id());
     mData->mActivityNotification->setInt32("generation", mData->mGeneration);
     requestActivityNotification(mData);
-    return OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-int AMediaCodec_stop(AMediaCodec *mData) {
-    int ret = translate_error(mData->mCodec->stop());
+media_status_t AMediaCodec_stop(AMediaCodec *mData) {
+    media_status_t ret = translate_error(mData->mCodec->stop());
 
     sp<AMessage> msg = new AMessage(kWhatStopActivityNotifications, mData->mHandler->id());
     sp<AMessage> response;
@@ -234,7 +236,7 @@
 }
 
 EXPORT
-int AMediaCodec_flush(AMediaCodec *mData) {
+media_status_t AMediaCodec_flush(AMediaCodec *mData) {
     return translate_error(mData->mCodec->flush());
 }
 
@@ -286,7 +288,7 @@
 }
 
 EXPORT
-int AMediaCodec_queueInputBuffer(AMediaCodec *mData,
+media_status_t AMediaCodec_queueInputBuffer(AMediaCodec *mData,
         size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags) {
 
     AString errorMsg;
@@ -332,7 +334,7 @@
 }
 
 EXPORT
-int AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) {
+media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) {
     if (render) {
         return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx));
     } else {
@@ -341,23 +343,30 @@
 }
 
 EXPORT
-int AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback, void *userdata) {
+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;
-    return OK;
+    return AMEDIA_OK;
 }
 
 typedef struct AMediaCodecCryptoInfo {
         int numsubsamples;
         uint8_t key[16];
         uint8_t iv[16];
-        uint32_t mode;
+        cryptoinfo_mode_t mode;
         size_t *clearbytes;
         size_t *encryptedbytes;
 } AMediaCodecCryptoInfo;
 
 EXPORT
-int AMediaCodec_queueSecureInputBuffer(
+media_status_t AMediaCodec_queueSecureInputBuffer(
         AMediaCodec* codec,
         size_t idx,
         off_t offset,
@@ -396,7 +405,7 @@
         int numsubsamples,
         uint8_t key[16],
         uint8_t iv[16],
-        uint32_t mode,
+        cryptoinfo_mode_t mode,
         size_t *clearbytes,
         size_t *encryptedbytes) {
 
@@ -424,9 +433,9 @@
 
 
 EXPORT
-int AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo* info) {
+media_status_t AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo* info) {
     free(info);
-    return OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
@@ -435,47 +444,59 @@
 }
 
 EXPORT
-int AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
-    if (!dst || !ci) {
-        return AMEDIAERROR_UNSUPPORTED;
+media_status_t AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
+    if (!ci) {
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+    if (!dst) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     memcpy(dst, ci->key, 16);
-    return OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-int AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
-    if (!dst || !ci) {
-        return AMEDIAERROR_UNSUPPORTED;
+media_status_t AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
+    if (!ci) {
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+    if (!dst) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     memcpy(dst, ci->iv, 16);
-    return OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-uint32_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo* ci) {
+cryptoinfo_mode_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo* ci) {
     if (!ci) {
-        return AMEDIAERROR_UNSUPPORTED;
+        return (cryptoinfo_mode_t) AMEDIA_ERROR_INVALID_OBJECT;
     }
     return ci->mode;
 }
 
 EXPORT
-int AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
-    if (!dst || !ci) {
-        return AMEDIAERROR_UNSUPPORTED;
+media_status_t AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
+    if (!ci) {
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+    if (!dst) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     memcpy(dst, ci->clearbytes, sizeof(size_t) * ci->numsubsamples);
-    return OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-int AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
-    if (!dst || !ci) {
-        return AMEDIAERROR_UNSUPPORTED;
+media_status_t AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
+    if (!ci) {
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+    if (!dst) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     memcpy(dst, ci->encryptedbytes, sizeof(size_t) * ci->numsubsamples);
-    return OK;
+    return AMEDIA_OK;
 }
 
 } // extern "C"
diff --git a/media/ndk/NdkMediaCrypto.cpp b/media/ndk/NdkMediaCrypto.cpp
index c686273..1cc2f1a 100644
--- a/media/ndk/NdkMediaCrypto.cpp
+++ b/media/ndk/NdkMediaCrypto.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaCrypto"
 
 
@@ -35,12 +35,12 @@
 
 using namespace android;
 
-static int translate_error(status_t err) {
+static media_status_t translate_error(status_t err) {
     if (err == OK) {
-        return OK;
+        return AMEDIA_OK;
     }
     ALOGE("sf error code: %d", err);
-    return -1000;
+    return AMEDIA_ERROR_UNKNOWN;
 }
 
 
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 5e50418..7a1048c 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaDrm"
 
 #include "NdkMediaDrm.h"
@@ -35,10 +35,20 @@
 
 typedef Vector<uint8_t> idvec_t;
 
+struct DrmListener: virtual public BnDrmClient
+{
+private:
+    AMediaDrm *mObj;
+    AMediaDrmEventListener mListener;
+
+public:
+    DrmListener(AMediaDrm *obj, AMediaDrmEventListener listener) : mObj(obj), mListener(listener) {}
+    void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj);
+};
+
 struct AMediaDrm {
     sp<IDrm> mDrm;
     sp<IDrmClient> mDrmClient;
-    AMediaDrmEventListener mListener;
     List<idvec_t> mIds;
     KeyedVector<String8, String8> mQueryResults;
     Vector<uint8_t> mKeyRequest;
@@ -47,42 +57,90 @@
     String8 mPropertyString;
     Vector<uint8_t> mPropertyByteArray;
     List<Vector<uint8_t> > mSecureStops;
+    sp<DrmListener> mListener;
 };
 
+void DrmListener::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
+    if (!mListener) {
+        return;
+    }
+
+    AMediaDrmSessionId sessionId = {NULL, 0};
+    int32_t sessionIdSize = obj->readInt32();
+    if (sessionIdSize) {
+        uint8_t *sessionIdData = new uint8_t[sessionIdSize];
+        sessionId.ptr = sessionIdData;
+        sessionId.length = sessionIdSize;
+        obj->read(sessionIdData, sessionId.length);
+    }
+
+    int32_t dataSize = obj->readInt32();
+    uint8_t *data = NULL;
+    if (dataSize) {
+        data = new uint8_t[dataSize];
+        obj->read(data, dataSize);
+    }
+
+    // translate DrmPlugin event types into their NDK equivalents
+    AMediaDrmEventType ndkEventType;
+    switch(eventType) {
+        case DrmPlugin::kDrmPluginEventProvisionRequired:
+            ndkEventType = EVENT_PROVISION_REQUIRED;
+            break;
+        case DrmPlugin::kDrmPluginEventKeyNeeded:
+            ndkEventType = EVENT_KEY_REQUIRED;
+            break;
+        case DrmPlugin::kDrmPluginEventKeyExpired:
+            ndkEventType = EVENT_KEY_EXPIRED;
+            break;
+        case DrmPlugin::kDrmPluginEventVendorDefined:
+            ndkEventType = EVENT_VENDOR_DEFINED;
+            break;
+        default:
+            ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
+            return;
+    }
+
+    (*mListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize);
+
+    delete [] sessionId.ptr;
+    delete [] data;
+}
+
+
 extern "C" {
 
-static mediadrm_status_t translateStatus(status_t status) {
-    mediadrm_status_t result = MEDIADRM_UNKNOWN_ERROR;
+static media_status_t translateStatus(status_t status) {
+    media_status_t result = AMEDIA_ERROR_UNKNOWN;
     switch (status) {
         case OK:
-            result = MEDIADRM_OK;
+            result = AMEDIA_OK;
             break;
         case android::ERROR_DRM_NOT_PROVISIONED:
-            result = MEDIADRM_NOT_PROVISIONED_ERROR;
+            result = AMEDIA_DRM_NOT_PROVISIONED;
             break;
         case android::ERROR_DRM_RESOURCE_BUSY:
-            result = MEDIADRM_RESOURCE_BUSY_ERROR;
+            result = AMEDIA_DRM_RESOURCE_BUSY;
             break;
         case android::ERROR_DRM_DEVICE_REVOKED:
-            result = MEDIADRM_DEVICE_REVOKED_ERROR;
+            result = AMEDIA_DRM_DEVICE_REVOKED;
             break;
         case android::ERROR_DRM_CANNOT_HANDLE:
-            result = MEDIADRM_INVALID_PARAMETER_ERROR;
+            result = AMEDIA_ERROR_INVALID_PARAMETER;
             break;
         case android::ERROR_DRM_TAMPER_DETECTED:
-            result = MEDIADRM_TAMPER_DETECTED_ERROR;
+            result = AMEDIA_DRM_TAMPER_DETECTED;
             break;
         case android::ERROR_DRM_SESSION_NOT_OPENED:
-            result = MEDIADRM_SESSION_NOT_OPENED_ERROR;
+            result = AMEDIA_DRM_SESSION_NOT_OPENED;
             break;
         case android::ERROR_DRM_NO_LICENSE:
-            result = MEDIADRM_NEED_KEY_ERROR;
+            result = AMEDIA_DRM_NEED_KEY;
             break;
         case android::ERROR_DRM_LICENSE_EXPIRED:
-            result = MEDIADRM_LICENSE_EXPIRED_ERROR;
+            result = AMEDIA_DRM_LICENSE_EXPIRED;
             break;
         default:
-            result = MEDIADRM_UNKNOWN_ERROR;
             break;
     }
     return result;
@@ -156,11 +214,15 @@
     delete mObj;
 }
 
-#if 0
-void AMediaDrm_setOnEventListener(AMediaDrm *mObj, AMediaDrmEventListener listener) {
-    mObj->mListener = listener;
+EXPORT
+media_status_t AMediaDrm_setOnEventListener(AMediaDrm *mObj, AMediaDrmEventListener listener) {
+    if (!mObj || mObj->mDrm == NULL) {
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+    mObj->mListener = new DrmListener(mObj, listener);
+    mObj->mDrm->setListener(mObj->mListener);
+    return AMEDIA_OK;
 }
-#endif
 
 
 static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
@@ -174,52 +236,58 @@
 }
 
 EXPORT
-mediadrm_status_t AMediaDrm_openSession(AMediaDrm *mObj, AMediaDrmSessionId &sessionId) {
+media_status_t AMediaDrm_openSession(AMediaDrm *mObj, AMediaDrmSessionId *sessionId) {
     if (!mObj || mObj->mDrm == NULL) {
-        return MEDIADRM_INVALID_OBJECT_ERROR;
+        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 MEDIADRM_OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-mediadrm_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 MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+    if (!sessionId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, sessionId, iter)) {
-        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    if (!findId(mObj, *sessionId, iter)) {
+        return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
     mObj->mDrm->closeSession(*iter);
     mObj->mIds.erase(iter);
-    return MEDIADRM_OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-mediadrm_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 MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
     }
-    if (!mimeType) {
-        return MEDIADRM_INVALID_PARAMETER_ERROR;
+    if (!mimeType || !scope || !keyRequest || !keyRequestSize) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, scope, iter)) {
-        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    if (!findId(mObj, *scope, iter)) {
+        return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
 
     Vector<uint8_t> mdInit;
@@ -236,7 +304,7 @@
             mdKeyType = DrmPlugin::kKeyType_Release;
             break;
         default:
-            return MEDIADRM_INVALID_PARAMETER_ERROR;
+            return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     KeyedVector<String8, String8> mdOptionalParameters;
     for (size_t i = 0; i < numOptionalParameters; i++) {
@@ -249,26 +317,26 @@
     if (status != OK) {
         return translateStatus(status);
     } else {
-        keyRequest = mObj->mKeyRequest.array();
-        keyRequestSize = mObj->mKeyRequest.size();
+        *keyRequest = mObj->mKeyRequest.array();
+        *keyRequestSize = mObj->mKeyRequest.size();
     }
-    return MEDIADRM_OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-mediadrm_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 MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
     }
-    if (!response || !responseSize) {
-        return MEDIADRM_INVALID_PARAMETER_ERROR;
+    if (!scope || !response || !responseSize || !keySetId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, scope, iter)) {
-        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    if (!findId(mObj, *scope, iter)) {
+        return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
     Vector<uint8_t> mdResponse;
     mdResponse.appendArray(response, responseSize);
@@ -278,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 MEDIADRM_OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-mediadrm_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 MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+    if (!sessionId || !keySetId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, sessionId, iter)) {
-        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    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
-mediadrm_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 MEDIADRM_INVALID_OBJECT_ERROR;
+        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);
@@ -322,44 +396,47 @@
 }
 
 EXPORT
-mediadrm_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 MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+    if (!sessionId || !numPairs) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, sessionId, iter)) {
-        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    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();
-        return MEDIADRM_SHORT_BUFFER;
+    if (mObj->mQueryResults.size() > *numPairs) {
+        *numPairs = mObj->mQueryResults.size();
+        return AMEDIA_DRM_SHORT_BUFFER;
     }
 
     for (size_t i = 0; i < mObj->mQueryResults.size(); i++) {
         keyValuePairs[i].mKey = mObj->mQueryResults.keyAt(i).string();
         keyValuePairs[i].mValue = mObj->mQueryResults.keyAt(i).string();
     }
-    numPairs = mObj->mQueryResults.size();
-    return MEDIADRM_OK;
+    *numPairs = mObj->mQueryResults.size();
+    return AMEDIA_OK;
 }
 
 EXPORT
-mediadrm_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 MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
     }
-    if (!provisionRequestSize || !serverUrl) {
-        return MEDIADRM_INVALID_PARAMETER_ERROR;
+    if (!provisionRequest || !provisionRequestSize || !*provisionRequestSize || !serverUrl) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
     status_t status = mObj->mDrm->getProvisionRequest(String8(""), String8(""),
@@ -367,21 +444,21 @@
     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 MEDIADRM_OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-mediadrm_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *mObj,
+media_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *mObj,
         const uint8_t *response, size_t responseSize) {
     if (!mObj || mObj->mDrm == NULL) {
-        return MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
     }
     if (!response || !responseSize) {
-        return MEDIADRM_INVALID_PARAMETER_ERROR;
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
     Vector<uint8_t> mdResponse;
@@ -392,19 +469,22 @@
 }
 
 EXPORT
-mediadrm_status_t AMediaDrm_getSecureStops(AMediaDrm *mObj,
-        AMediaDrmSecureStop *secureStops, size_t &numSecureStops) {
+media_status_t AMediaDrm_getSecureStops(AMediaDrm *mObj,
+        AMediaDrmSecureStop *secureStops, size_t *numSecureStops) {
 
     if (!mObj || mObj->mDrm == NULL) {
-        return MEDIADRM_INVALID_OBJECT_ERROR;
+        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()) {
-        return MEDIADRM_SHORT_BUFFER;
+    if (*numSecureStops < mObj->mSecureStops.size()) {
+        return AMEDIA_DRM_SHORT_BUFFER;
     }
     List<Vector<uint8_t> >::iterator iter = mObj->mSecureStops.begin();
     size_t i = 0;
@@ -414,68 +494,77 @@
         ++iter;
         ++i;
     }
-    numSecureStops = mObj->mSecureStops.size();
-    return MEDIADRM_OK;
+    *numSecureStops = mObj->mSecureStops.size();
+    return AMEDIA_OK;
 }
 
 EXPORT
-mediadrm_status_t AMediaDrm_releaseSecureStops(AMediaDrm *mObj,
-        const AMediaDrmSecureStop &ssRelease) {
+media_status_t AMediaDrm_releaseSecureStops(AMediaDrm *mObj,
+        const AMediaDrmSecureStop *ssRelease) {
 
     if (!mObj || mObj->mDrm == NULL) {
-        return MEDIADRM_INVALID_OBJECT_ERROR;
+        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
-mediadrm_status_t AMediaDrm_getPropertyString(AMediaDrm *mObj, const char *propertyName,
-        const char *&propertyValue) {
+media_status_t AMediaDrm_getPropertyString(AMediaDrm *mObj, const char *propertyName,
+        const char **propertyValue) {
 
     if (!mObj || mObj->mDrm == NULL) {
-        return MEDIADRM_INVALID_OBJECT_ERROR;
+        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
-mediadrm_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *mObj,
-        const char *propertyName, AMediaDrmByteArray &propertyValue) {
+media_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *mObj,
+        const char *propertyName, AMediaDrmByteArray *propertyValue) {
     if (!mObj || mObj->mDrm == NULL) {
-        return MEDIADRM_INVALID_OBJECT_ERROR;
+        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);
 }
 
 EXPORT
-mediadrm_status_t AMediaDrm_setPropertyString(AMediaDrm *mObj,
+media_status_t AMediaDrm_setPropertyString(AMediaDrm *mObj,
         const char *propertyName, const char *value) {
     if (!mObj || mObj->mDrm == NULL) {
-        return MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
     }
 
     return translateStatus(mObj->mDrm->setPropertyString(String8(propertyName),
@@ -483,7 +572,7 @@
 }
 
 EXPORT
-mediadrm_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *mObj,
+media_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *mObj,
         const char *propertyName, const uint8_t *value, size_t valueSize) {
 
     Vector<uint8_t> byteArray;
@@ -494,17 +583,17 @@
 }
 
 
-static mediadrm_status_t encrypt_decrypt_common(AMediaDrm *mObj,
+static media_status_t encrypt_decrypt_common(AMediaDrm *mObj,
         const AMediaDrmSessionId &sessionId,
         const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
         const uint8_t *input, uint8_t *output, size_t dataSize, bool encrypt) {
 
     if (!mObj || mObj->mDrm == NULL) {
-        return MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
     }
     List<idvec_t>::iterator iter;
     if (!findId(mObj, sessionId, iter)) {
-        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+        return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
 
     status_t status = mObj->mDrm->setCipherAlgorithm(*iter, String8(cipherAlgorithm));
@@ -536,32 +625,41 @@
 }
 
 EXPORT
-mediadrm_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
-mediadrm_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
-mediadrm_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 MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+    if (!sessionId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, sessionId, iter)) {
-        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    if (!findId(mObj, *sessionId, iter)) {
+        return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
 
     status_t status = mObj->mDrm->setMacAlgorithm(*iter, String8(macAlgorithm));
@@ -579,7 +677,7 @@
     Vector<uint8_t> signatureVec;
     status = mObj->mDrm->sign(*iter, keyIdVec, messageVec, signatureVec);
     if (signatureVec.size() > *signatureSize) {
-        return MEDIADRM_SHORT_BUFFER;
+        return AMEDIA_DRM_SHORT_BUFFER;
     }
     if (status == OK) {
         memcpy(signature, signatureVec.array(), signatureVec.size());
@@ -588,16 +686,19 @@
 }
 
 EXPORT
-mediadrm_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 MEDIADRM_INVALID_OBJECT_ERROR;
+        return AMEDIA_ERROR_INVALID_OBJECT;
+    }
+    if (!sessionId) {
+        return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     List<idvec_t>::iterator iter;
-    if (!findId(mObj, sessionId, iter)) {
-        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    if (!findId(mObj, *sessionId, iter)) {
+        return AMEDIA_DRM_SESSION_NOT_OPENED;
     }
 
     status_t status = mObj->mDrm->setMacAlgorithm(*iter, String8(macAlgorithm));
@@ -618,7 +719,7 @@
     bool match;
     status = mObj->mDrm->verify(*iter, keyIdVec, messageVec, signatureVec, match);
     if (status == OK) {
-        return match ? MEDIADRM_OK : MEDIADRM_VERIFY_FAILED;
+        return match ? AMEDIA_OK : AMEDIA_DRM_VERIFY_FAILED;
     }
     return translateStatus(status);
 }
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index e23adf3..492e002 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaExtractor"
 
 
@@ -38,12 +38,12 @@
 
 using namespace android;
 
-static int translate_error(status_t err) {
+static media_status_t translate_error(status_t err) {
     if (err == OK) {
-        return OK;
+        return AMEDIA_OK;
     }
     ALOGE("sf error code: %d", err);
-    return AMEDIAERROR_GENERIC;
+    return AMEDIA_ERROR_UNKNOWN;
 }
 
 struct AMediaExtractor {
@@ -63,21 +63,20 @@
 }
 
 EXPORT
-int AMediaExtractor_delete(AMediaExtractor *mData) {
+media_status_t AMediaExtractor_delete(AMediaExtractor *mData) {
     ALOGV("dtor");
     delete mData;
-    return OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-int AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, off64_t offset, off64_t length) {
+media_status_t AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, off64_t offset, off64_t length) {
     ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
-    mData->mImpl->setDataSource(fd, offset, length);
-    return 0;
+    return translate_error(mData->mImpl->setDataSource(fd, offset, length));
 }
 
 EXPORT
-int AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) {
+media_status_t AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) {
     ALOGV("setDataSource(%s)", location);
     // TODO: add header support
 
@@ -86,14 +85,14 @@
     if (env == NULL) {
         ALOGE("setDataSource(path) must be called from Java thread");
         env->ExceptionClear();
-        return AMEDIAERROR_UNSUPPORTED;
+        return AMEDIA_ERROR_UNSUPPORTED;
     }
 
     jclass mediahttpclass = env->FindClass("android/media/MediaHTTPService");
     if (mediahttpclass == NULL) {
         ALOGE("can't find MediaHttpService");
         env->ExceptionClear();
-        return AMEDIAERROR_UNSUPPORTED;
+        return AMEDIA_ERROR_UNSUPPORTED;
     }
 
     jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass,
@@ -101,7 +100,7 @@
     if (mediaHttpCreateMethod == NULL) {
         ALOGE("can't find method");
         env->ExceptionClear();
-        return AMEDIAERROR_UNSUPPORTED;
+        return AMEDIA_ERROR_UNSUPPORTED;
     }
 
     jstring jloc = env->NewStringUTF(location);
@@ -115,13 +114,13 @@
         httpService = interface_cast<IMediaHTTPService>(binder);
     }
 
-    mData->mImpl->setDataSource(httpService, location, NULL);
+    status_t err = mData->mImpl->setDataSource(httpService, location, NULL);
     env->ExceptionClear();
-    return OK;
+    return translate_error(err);
 }
 
 EXPORT
-int AMediaExtractor_getTrackCount(AMediaExtractor *mData) {
+size_t AMediaExtractor_getTrackCount(AMediaExtractor *mData) {
     return mData->mImpl->countTracks();
 }
 
@@ -133,13 +132,13 @@
 }
 
 EXPORT
-int AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) {
+media_status_t AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) {
     ALOGV("selectTrack(%z)", idx);
     return translate_error(mData->mImpl->selectTrack(idx));
 }
 
 EXPORT
-int AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) {
+media_status_t AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) {
     ALOGV("unselectTrack(%z)", idx);
     return translate_error(mData->mImpl->unselectTrack(idx));
 }
@@ -151,7 +150,21 @@
 }
 
 EXPORT
-int AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) {
+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);
     if (mData->mImpl->readSampleData(tmp) == OK) {
@@ -161,7 +174,7 @@
 }
 
 EXPORT
-int AMediaExtractor_getSampleFlags(AMediaExtractor *mData) {
+uint32_t AMediaExtractor_getSampleFlags(AMediaExtractor *mData) {
     int sampleFlags = 0;
     sp<MetaData> meta;
     status_t err = mData->mImpl->getSampleMeta(&meta);
@@ -170,14 +183,14 @@
     }
     int32_t val;
     if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
-        sampleFlags |= NuMediaExtractor::SAMPLE_FLAG_SYNC;
+        sampleFlags |= AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC;
     }
 
     uint32_t type;
     const void *data;
     size_t size;
     if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
-        sampleFlags |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED;
+        sampleFlags |= AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED;
     }
     return sampleFlags;
 }
@@ -192,7 +205,7 @@
 }
 
 EXPORT
-int64_t AMediaExtractor_getSampletime(AMediaExtractor *mData) {
+int64_t AMediaExtractor_getSampleTime(AMediaExtractor *mData) {
     int64_t time;
     if (mData->mImpl->getSampleTime(&time) != OK) {
         return -1;
@@ -332,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/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index e1d8c95..67dc2c2 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaFormat"
 
 
@@ -64,10 +64,10 @@
 }
 
 EXPORT
-int AMediaFormat_delete(AMediaFormat *mData) {
+media_status_t AMediaFormat_delete(AMediaFormat *mData) {
     ALOGV("dtor");
     delete mData;
-    return OK;
+    return AMEDIA_OK;
 }
 
 
diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp
index aa78740..b1b0362 100644
--- a/media/ndk/NdkMediaMuxer.cpp
+++ b/media/ndk/NdkMediaMuxer.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaMuxer"
 
 
@@ -37,12 +37,12 @@
 
 using namespace android;
 
-static int translate_error(status_t err) {
+static media_status_t translate_error(status_t err) {
     if (err == OK) {
-        return OK;
+        return AMEDIA_OK;
     }
     ALOGE("sf error code: %d", err);
-    return -1000;
+    return AMEDIA_ERROR_UNKNOWN;
 }
 
 struct AMediaMuxer {
@@ -61,19 +61,19 @@
 }
 
 EXPORT
-int AMediaMuxer_delete(AMediaMuxer *muxer) {
+media_status_t AMediaMuxer_delete(AMediaMuxer *muxer) {
     ALOGV("dtor");
     delete muxer;
-    return OK;
+    return AMEDIA_OK;
 }
 
 EXPORT
-int AMediaMuxer_setLocation(AMediaMuxer *muxer, float latitude, float longtitude) {
+media_status_t AMediaMuxer_setLocation(AMediaMuxer *muxer, float latitude, float longtitude) {
     return translate_error(muxer->mImpl->setLocation(latitude * 10000, longtitude * 10000));
 }
 
 EXPORT
-int AMediaMuxer_setOrientationHint(AMediaMuxer *muxer, int degrees) {
+media_status_t AMediaMuxer_setOrientationHint(AMediaMuxer *muxer, int degrees) {
     return translate_error(muxer->mImpl->setOrientationHint(degrees));
 }
 
@@ -85,21 +85,21 @@
 }
 
 EXPORT
-int AMediaMuxer_start(AMediaMuxer *muxer) {
+media_status_t AMediaMuxer_start(AMediaMuxer *muxer) {
     return translate_error(muxer->mImpl->start());
 }
 
 EXPORT
-int AMediaMuxer_stop(AMediaMuxer *muxer) {
+media_status_t AMediaMuxer_stop(AMediaMuxer *muxer) {
     return translate_error(muxer->mImpl->stop());
 }
 
 EXPORT
-int 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);
+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);
     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/Android.mk b/services/audioflinger/Android.mk
index 8d0a705..0bdf5a3 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -29,6 +29,7 @@
     Tracks.cpp                  \
     Effects.cpp                 \
     AudioMixer.cpp.arm          \
+    PatchPanel.cpp
 
 LOCAL_SRC_FILES += StateQueue.cpp
 
@@ -63,6 +64,7 @@
 
 LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp
 LOCAL_SRC_FILES += FastThread.cpp FastThreadState.cpp
+LOCAL_SRC_FILES += FastCapture.cpp FastCaptureState.cpp
 
 LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
 
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 11170c2..5b09d54 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;
@@ -177,6 +177,7 @@
     if (doLog) {
         mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters", MemoryHeapBase::READ_ONLY);
     }
+
 #ifdef TEE_SINK
     (void) property_get("ro.debuggable", value, "0");
     int debuggable = atoi(value);
@@ -218,6 +219,8 @@
         }
     }
 
+    mPatchPanel = new PatchPanel(this);
+
     mMode = AUDIO_MODE_NORMAL;
 }
 
@@ -427,7 +430,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 +638,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 +1180,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 +1426,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;
     }
@@ -1837,7 +1848,8 @@
     if (status == BAD_VALUE &&
         reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&
         (config.sample_rate <= 2 * reqSamplingRate) &&
-        (popcount(config.channel_mask) <= FCC_2) && (popcount(reqChannelMask) <= FCC_2)) {
+        (audio_channel_count_from_in_mask(config.channel_mask) <= FCC_2) &&
+        (audio_channel_count_from_in_mask(reqChannelMask) <= FCC_2)) {
         // FIXME describe the change proposed by HAL (save old values so we can log them here)
         ALOGV("openInput() reopening with proposed sampling rate and channel mask");
         inStream = NULL;
@@ -1857,7 +1869,8 @@
             TEE_SINK_OLD,   // copy input using an existing pipe
         } kind;
         NBAIO_Format format = Format_from_SR_C(inStream->common.get_sample_rate(&inStream->common),
-                                        popcount(inStream->common.get_channels(&inStream->common)));
+                audio_channel_count_from_in_mask(
+                        inStream->common.get_channels(&inStream->common)));
         if (!mTeeSinkInputEnabled) {
             kind = TEE_SINK_NO;
         } else if (!Format_isValid(format)) {
@@ -2378,6 +2391,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:
@@ -2588,7 +2606,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];
@@ -2642,11 +2660,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..29dc6b2 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 :
@@ -226,6 +223,27 @@
 
     virtual status_t setLowRamDevice(bool isLowRamDevice);
 
+    /* List available audio ports and their attributes */
+    virtual status_t listAudioPorts(unsigned int *num_ports,
+                                    struct audio_port *ports);
+
+    /* Get attributes for a given audio port */
+    virtual status_t getAudioPort(struct audio_port *port);
+
+    /* Create an audio patch between several source and sink ports */
+    virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle);
+
+    /* Release an audio patch */
+    virtual status_t releaseAudioPatch(audio_patch_handle_t handle);
+
+    /* List existing audio patches */
+    virtual status_t listAudioPatches(unsigned int *num_patches,
+                                      struct audio_patch *patches);
+
+    /* Set audio port configuration */
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+
     virtual     status_t    onTransact(
                                 uint32_t code,
                                 const Parcel& data,
@@ -400,6 +418,8 @@
 
 #include "Effects.h"
 
+#include "PatchPanel.h"
+
     // server side of the client's IAudioTrack
     class TrackHandle : public android::BnAudioTrack {
     public:
@@ -507,6 +527,8 @@
 
         const char *moduleName() const { return mModuleName; }
         audio_hw_device_t *hwDevice() const { return mHwDevice; }
+        uint32_t version() const { return mHwDevice->common.version; }
+
     private:
         const char * const mModuleName;
         audio_hw_device_t * const mHwDevice;
@@ -667,6 +689,8 @@
     bool    mIsLowRamDevice;
     bool    mIsDeviceTypeKnown;
     nsecs_t mGlobalEffectEnableTime;  // when a global effect was last enabled
+
+    sp<PatchPanel> mPatchPanel;
 };
 
 #undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 2d67efb..a0f2b7d 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -34,6 +34,7 @@
 #include <system/audio.h>
 
 #include <audio_utils/primitives.h>
+#include <audio_utils/format.h>
 #include <common_time/local_clock.h>
 #include <common_time/cc_helper.h>
 
@@ -88,6 +89,103 @@
     }
 }
 
+template <typename T>
+T min(const T& a, const T& b)
+{
+    return a < b ? a : b;
+}
+
+AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels,
+        audio_format_t inputFormat, audio_format_t outputFormat) :
+        mTrackBufferProvider(NULL),
+        mChannels(channels),
+        mInputFormat(inputFormat),
+        mOutputFormat(outputFormat),
+        mInputFrameSize(channels * audio_bytes_per_sample(inputFormat)),
+        mOutputFrameSize(channels * audio_bytes_per_sample(outputFormat)),
+        mOutputData(NULL),
+        mOutputCount(0),
+        mConsumed(0)
+{
+    ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat);
+    if (requiresInternalBuffers()) {
+        mOutputCount = 256;
+        (void)posix_memalign(&mOutputData, 32, mOutputCount * mOutputFrameSize);
+    }
+    mBuffer.frameCount = 0;
+}
+
+AudioMixer::ReformatBufferProvider::~ReformatBufferProvider()
+{
+    ALOGV("~ReformatBufferProvider(%p)", this);
+    if (mBuffer.frameCount != 0) {
+        mTrackBufferProvider->releaseBuffer(&mBuffer);
+    }
+    free(mOutputData);
+}
+
+status_t AudioMixer::ReformatBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
+        int64_t pts) {
+    //ALOGV("ReformatBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+    //        this, pBuffer, pBuffer->frameCount, pts);
+    if (!requiresInternalBuffers()) {
+        status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+        if (res == OK) {
+            memcpy_by_audio_format(pBuffer->raw, mOutputFormat, pBuffer->raw, mInputFormat,
+                    pBuffer->frameCount * mChannels);
+        }
+        return res;
+    }
+    if (mBuffer.frameCount == 0) {
+        mBuffer.frameCount = pBuffer->frameCount;
+        status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+        // TODO: Track down a bug in the upstream provider
+        // LOG_ALWAYS_FATAL_IF(res == OK && mBuffer.frameCount == 0,
+        //        "ReformatBufferProvider::getNextBuffer():"
+        //        " Invalid zero framecount returned from getNextBuffer()");
+        if (res != OK || mBuffer.frameCount == 0) {
+            pBuffer->raw = NULL;
+            pBuffer->frameCount = 0;
+            return res;
+        }
+    }
+    ALOG_ASSERT(mConsumed < mBuffer.frameCount);
+    size_t count = min(mOutputCount, mBuffer.frameCount - mConsumed);
+    count = min(count, pBuffer->frameCount);
+    pBuffer->raw = mOutputData;
+    pBuffer->frameCount = count;
+    //ALOGV("reformatting %d frames from %#x to %#x, %d chan",
+    //        pBuffer->frameCount, mInputFormat, mOutputFormat, mChannels);
+    memcpy_by_audio_format(pBuffer->raw, mOutputFormat,
+            (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize, mInputFormat,
+            pBuffer->frameCount * mChannels);
+    return OK;
+}
+
+void AudioMixer::ReformatBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
+    //ALOGV("ReformatBufferProvider(%p)::releaseBuffer(%p(%zu))",
+    //        this, pBuffer, pBuffer->frameCount);
+    if (!requiresInternalBuffers()) {
+        mTrackBufferProvider->releaseBuffer(pBuffer);
+        return;
+    }
+    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
+    mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
+    if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
+        mConsumed = 0;
+        mTrackBufferProvider->releaseBuffer(&mBuffer);
+        // ALOG_ASSERT(mBuffer.frameCount == 0);
+    }
+    pBuffer->raw = NULL;
+    pBuffer->frameCount = 0;
+}
+
+void AudioMixer::ReformatBufferProvider::reset() {
+    if (mBuffer.frameCount != 0) {
+        mTrackBufferProvider->releaseBuffer(&mBuffer);
+    }
+    mConsumed = 0;
+}
 
 // ----------------------------------------------------------------------------
 bool AudioMixer::sIsMultichannelCapable = false;
@@ -153,13 +251,17 @@
     mState.mLog = log;
 }
 
-int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId)
+int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
+        audio_format_t format, int sessionId)
 {
+    if (!isValidPcmTrackFormat(format)) {
+        ALOGE("AudioMixer::getTrackName invalid format (%#x)", format);
+        return -1;
+    }
     uint32_t names = (~mTrackNames) & mConfiguredNames;
     if (names != 0) {
         int n = __builtin_ctz(names);
         ALOGV("add track (%d)", n);
-        mTrackNames |= 1 << n;
         // assume default parameters for the track, except where noted below
         track_t* t = &mState.tracks[n];
         t->needs = 0;
@@ -175,10 +277,11 @@
         // no initialization needed
         // t->prevAuxLevel
         // t->frameCount
-        t->channelCount = 2;
+        t->channelCount = audio_channel_count_from_out_mask(channelMask);
         t->enabled = false;
-        t->format = 16;
-        t->channelMask = AUDIO_CHANNEL_OUT_STEREO;
+        ALOGV_IF(channelMask != AUDIO_CHANNEL_OUT_STEREO,
+                "Non-stereo channel mask: %d\n", channelMask);
+        t->channelMask = channelMask;
         t->sessionId = sessionId;
         // setBufferProvider(name, AudioBufferProvider *) is required before enable(name)
         t->bufferProvider = NULL;
@@ -192,16 +295,24 @@
         // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name)
         t->mainBuffer = NULL;
         t->auxBuffer = NULL;
+        t->mInputBufferProvider = NULL;
+        t->mReformatBufferProvider = NULL;
         t->downmixerBufferProvider = NULL;
         t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
-
-        status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask);
-        if (status == OK) {
-            return TRACK0 + n;
+        t->mFormat = format;
+        t->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT;
+        if (t->mFormat != t->mMixerInFormat) {
+            prepareTrackForReformat(t, n);
         }
-        ALOGE("AudioMixer::getTrackName(0x%x) failed, error preparing track for downmix",
-                channelMask);
+        status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask);
+        if (status != OK) {
+            ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
+            return -1;
+        }
+        mTrackNames |= 1 << n;
+        return TRACK0 + n;
     }
+    ALOGE("AudioMixer::getTrackName out of available tracks");
     return -1;
 }
 
@@ -215,7 +326,7 @@
 
 status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask)
 {
-    uint32_t channelCount = popcount(mask);
+    uint32_t channelCount = audio_channel_count_from_out_mask(mask);
     ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount);
     status_t status = OK;
     if (channelCount > MAX_NUM_CHANNELS) {
@@ -236,9 +347,9 @@
     if (pTrack->downmixerBufferProvider != NULL) {
         // this track had previously been configured with a downmixer, delete it
         ALOGV(" deleting old downmixer");
-        pTrack->bufferProvider = pTrack->downmixerBufferProvider->mTrackBufferProvider;
         delete pTrack->downmixerBufferProvider;
         pTrack->downmixerBufferProvider = NULL;
+        reconfigureBufferProviders(pTrack);
     } else {
         ALOGV(" nothing to do, no downmixer to delete");
     }
@@ -332,21 +443,51 @@
     }// end of scope for local variables that are not used in goto label "noDownmixForActiveTrack"
 
     // initialization successful:
-    // - keep track of the real buffer provider in case it was set before
-    pDbp->mTrackBufferProvider = pTrack->bufferProvider;
-    // - we'll use the downmix effect integrated inside this
-    //    track's buffer provider, and we'll use it as the track's buffer provider
     pTrack->downmixerBufferProvider = pDbp;
-    pTrack->bufferProvider = pDbp;
-
+    reconfigureBufferProviders(pTrack);
     return NO_ERROR;
 
 noDownmixForActiveTrack:
     delete pDbp;
     pTrack->downmixerBufferProvider = NULL;
+    reconfigureBufferProviders(pTrack);
     return NO_INIT;
 }
 
+void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) {
+    ALOGV("AudioMixer::unprepareTrackForReformat(%d)", trackName);
+    if (pTrack->mReformatBufferProvider != NULL) {
+        delete pTrack->mReformatBufferProvider;
+        pTrack->mReformatBufferProvider = NULL;
+        reconfigureBufferProviders(pTrack);
+    }
+}
+
+status_t AudioMixer::prepareTrackForReformat(track_t* pTrack, int trackName)
+{
+    ALOGV("AudioMixer::prepareTrackForReformat(%d) with format %#x", trackName, pTrack->mFormat);
+    // discard the previous reformatter if there was one
+     unprepareTrackForReformat(pTrack, trackName);
+     pTrack->mReformatBufferProvider = new ReformatBufferProvider(
+             audio_channel_count_from_out_mask(pTrack->channelMask),
+             pTrack->mFormat, pTrack->mMixerInFormat);
+     reconfigureBufferProviders(pTrack);
+     return NO_ERROR;
+}
+
+void AudioMixer::reconfigureBufferProviders(track_t* pTrack)
+{
+    pTrack->bufferProvider = pTrack->mInputBufferProvider;
+    if (pTrack->mReformatBufferProvider) {
+        pTrack->mReformatBufferProvider->mTrackBufferProvider = pTrack->bufferProvider;
+        pTrack->bufferProvider = pTrack->mReformatBufferProvider;
+    }
+    if (pTrack->downmixerBufferProvider) {
+        pTrack->downmixerBufferProvider->mTrackBufferProvider = pTrack->bufferProvider;
+        pTrack->bufferProvider = pTrack->downmixerBufferProvider;
+    }
+}
+
 void AudioMixer::deleteTrackName(int name)
 {
     ALOGV("AudioMixer::deleteTrackName(%d)", name);
@@ -363,6 +504,8 @@
     track.resampler = NULL;
     // delete the downmixer
     unprepareTrackForDownmix(&mState.tracks[name], name);
+    // delete the reformatter
+    unprepareTrackForReformat(&mState.tracks[name], name);
 
     mTrackNames &= ~(1<<name);
 }
@@ -410,7 +553,7 @@
             audio_channel_mask_t mask =
                 static_cast<audio_channel_mask_t>(reinterpret_cast<uintptr_t>(value));
             if (track.channelMask != mask) {
-                uint32_t channelCount = popcount(mask);
+                uint32_t channelCount = audio_channel_count_from_out_mask(mask);
                 ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount);
                 track.channelMask = mask;
                 track.channelCount = channelCount;
@@ -434,9 +577,20 @@
                 invalidateState(1 << name);
             }
             break;
-        case FORMAT:
-            ALOG_ASSERT(valueInt == AUDIO_FORMAT_PCM_16_BIT);
-            break;
+        case FORMAT: {
+            audio_format_t format = static_cast<audio_format_t>(valueInt);
+            if (track.mFormat != format) {
+                ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
+                track.mFormat = format;
+                ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
+                //if (track.mFormat != track.mMixerInFormat)
+                {
+                    ALOGD("Reformatting!");
+                    prepareTrackForReformat(&track, name);
+                }
+                invalidateState(1 << name);
+            }
+            } break;
         // FIXME do we want to support setting the downmix type from AudioFlinger?
         //         for a specific track? or per mixer?
         /* case DOWNMIX_TYPE:
@@ -549,8 +703,9 @@
                 } else {
                     quality = AudioResampler::DEFAULT_QUALITY;
                 }
+                const int bits = mMixerInFormat == AUDIO_FORMAT_PCM_16_BIT ? 16 : /* FLOAT */ 32;
                 resampler = AudioResampler::create(
-                        format,
+                        bits,
                         // the resampler sees the number of channels after the downmixer, if any
                         (int) (downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount),
                         devSampleRate, quality);
@@ -595,21 +750,16 @@
     name -= TRACK0;
     ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
 
-    if (mState.tracks[name].downmixerBufferProvider != NULL) {
-        // update required?
-        if (mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider != bufferProvider) {
-            ALOGV("AudioMixer::setBufferProvider(%p) for downmix", bufferProvider);
-            // setting the buffer provider for a track that gets downmixed consists in:
-            //  1/ setting the buffer provider to the "downmix / buffer provider" wrapper
-            //     so it's the one that gets called when the buffer provider is needed,
-            mState.tracks[name].bufferProvider = mState.tracks[name].downmixerBufferProvider;
-            //  2/ saving the buffer provider for the track so the wrapper can use it
-            //     when it downmixes.
-            mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider = bufferProvider;
-        }
-    } else {
-        mState.tracks[name].bufferProvider = bufferProvider;
+    if (mState.tracks[name].mInputBufferProvider == bufferProvider) {
+        return; // don't reset any buffer providers if identical.
     }
+    if (mState.tracks[name].mReformatBufferProvider != NULL) {
+        mState.tracks[name].mReformatBufferProvider->reset();
+    } else if (mState.tracks[name].downmixerBufferProvider != NULL) {
+    }
+
+    mState.tracks[name].mInputBufferProvider = bufferProvider;
+    reconfigureBufferProviders(&mState.tracks[name]);
 }
 
 
@@ -1300,6 +1450,7 @@
     AudioBufferProvider::Buffer& b(t.buffer);
 
     int32_t* out = t.mainBuffer;
+    float *fout = reinterpret_cast<float*>(out);
     size_t numFrames = state->frameCount;
 
     const int16_t vl = t.volume[0];
@@ -1313,9 +1464,10 @@
 
         // in == NULL can happen if the track was flushed just after having
         // been enabled for mixing.
-        if (in == NULL || ((unsigned long)in & 3)) {
-            memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t));
-            ALOGE_IF(((unsigned long)in & 3), "process stereo track: input buffer alignment pb: "
+        if (in == NULL || (((uintptr_t)in) & 3)) {
+            memset(out, 0, numFrames
+                    * MAX_NUM_CHANNELS * audio_bytes_per_sample(t.mMixerFormat));
+            ALOGE_IF((((uintptr_t)in) & 3), "process stereo track: input buffer alignment pb: "
                                               "buffer %p track %d, channels %d, needs %08x",
                     in, i, t.channelCount, t.needs);
             return;
@@ -1323,8 +1475,7 @@
         size_t outFrames = b.frameCount;
 
         switch (t.mMixerFormat) {
-        case AUDIO_FORMAT_PCM_FLOAT: {
-            float *fout = reinterpret_cast<float*>(out);
+        case AUDIO_FORMAT_PCM_FLOAT:
             do {
                 uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
                 in += 2;
@@ -1335,7 +1486,7 @@
                 // Note: In case of later int16_t sink output,
                 // conversion and clamping is done by memcpy_to_i16_from_float().
             } while (--outFrames);
-            } break;
+            break;
         case AUDIO_FORMAT_PCM_16_BIT:
             if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
                 // volume is boosted, so we might need to clamp even though
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index e5e120c..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,
@@ -100,7 +104,10 @@
     // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
 
     // Allocate a track name.  Returns new track name if successful, -1 on failure.
-    int         getTrackName(audio_channel_mask_t channelMask, int sessionId);
+    // The failure could be because of an invalid channelMask or format, or that
+    // the track capacity of the mixer is exceeded.
+    int         getTrackName(audio_channel_mask_t channelMask,
+                             audio_format_t format, int sessionId);
 
     // Free an allocated track by name
     void        deleteTrackName(int name);
@@ -118,6 +125,13 @@
 
     size_t      getUnreleasedFrames(int name) const;
 
+    static inline bool isValidPcmTrackFormat(audio_format_t format) {
+        return format == AUDIO_FORMAT_PCM_16_BIT ||
+                format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
+                format == AUDIO_FORMAT_PCM_32_BIT ||
+                format == AUDIO_FORMAT_PCM_FLOAT;
+    }
+
 private:
 
     enum {
@@ -139,6 +153,7 @@
     struct state_t;
     struct track_t;
     class DownmixerBufferProvider;
+    class ReformatBufferProvider;
 
     typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
                            int32_t* aux);
@@ -166,7 +181,7 @@
         uint16_t    frameCount;
 
         uint8_t     channelCount;   // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
-        uint8_t     format;         // always 16
+        uint8_t     unused_padding; // formerly format, was always 16
         uint16_t    enabled;        // actually bool
         audio_channel_mask_t channelMask;
 
@@ -189,14 +204,19 @@
         int32_t*           auxBuffer;
 
         // 16-byte boundary
-
+        AudioBufferProvider*     mInputBufferProvider;    // 4 bytes
+        ReformatBufferProvider*  mReformatBufferProvider; // 4 bytes
         DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes
 
         int32_t     sessionId;
 
-        audio_format_t mMixerFormat; // at this time: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+        // 16-byte boundary
+        audio_format_t mMixerFormat;     // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+        audio_format_t mFormat;          // input track format
+        audio_format_t mMixerInFormat;   // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+                                         // each track must be converted to this format.
 
-        int32_t     padding[1];
+        int32_t        mUnused[1];       // alignment padding
 
         // 16-byte boundary
 
@@ -235,6 +255,35 @@
         effect_config_t    mDownmixConfig;
     };
 
+    // AudioBufferProvider wrapper that reformats track to acceptable mixer input type
+    class ReformatBufferProvider : public AudioBufferProvider {
+    public:
+        ReformatBufferProvider(int32_t channels,
+                audio_format_t inputFormat, audio_format_t outputFormat);
+        virtual ~ReformatBufferProvider();
+
+        // overrides AudioBufferProvider methods
+        virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
+        virtual void releaseBuffer(Buffer* buffer);
+
+        void reset();
+        inline bool requiresInternalBuffers() {
+            return true; //mInputFrameSize < mOutputFrameSize;
+        }
+
+        AudioBufferProvider* mTrackBufferProvider;
+        int32_t              mChannels;
+        audio_format_t       mInputFormat;
+        audio_format_t       mOutputFormat;
+        size_t               mInputFrameSize;
+        size_t               mOutputFrameSize;
+        // (only) required for reformatting to a larger size.
+        AudioBufferProvider::Buffer mBuffer;
+        void*                mOutputData;
+        size_t               mOutputCount;
+        size_t               mConsumed;
+    };
+
     // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
     uint32_t        mTrackNames;
 
@@ -262,6 +311,9 @@
     static status_t initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask);
     static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum);
     static void unprepareTrackForDownmix(track_t* pTrack, int trackName);
+    static status_t prepareTrackForReformat(track_t* pTrack, int trackNum);
+    static void unprepareTrackForReformat(track_t* pTrack, int trackName);
+    static void reconfigureBufferProviders(track_t* pTrack);
 
     static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
             int32_t* aux);
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
index 3abe8fd..a4446a4 100644
--- a/services/audioflinger/AudioResamplerDyn.cpp
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -455,12 +455,13 @@
     const Constants& c(mConstants);
     const TC* const coefs = mConstants.mFirCoefs;
     TI* impulse = mInBuffer.getImpulse();
-    size_t inputIndex = mInputIndex;
+    size_t inputIndex = 0;
     uint32_t phaseFraction = mPhaseFraction;
     const uint32_t phaseIncrement = mPhaseIncrement;
     size_t outputIndex = 0;
     size_t outputSampleCount = outFrameCount * 2;   // stereo output
-    size_t inFrameCount = getInFrameCountRequired(outFrameCount);
+    size_t inFrameCount = getInFrameCountRequired(outFrameCount) + (phaseFraction != 0);
+    ALOG_ASSERT(0 < inFrameCount && inFrameCount < (1U << 31));
     const uint32_t phaseWrapLimit = c.mL << c.mShift;
 
     // NOTE: be very careful when modifying the code here. register
@@ -474,11 +475,13 @@
         // buffer is empty, fetch a new one
         while (mBuffer.frameCount == 0) {
             mBuffer.frameCount = inFrameCount;
+            ALOG_ASSERT(inFrameCount > 0);
             provider->getNextBuffer(&mBuffer,
                     calculateOutputPTS(outputIndex / 2));
             if (mBuffer.raw == NULL) {
                 goto resample_exit;
             }
+            inFrameCount -= mBuffer.frameCount;
             if (phaseFraction >= phaseWrapLimit) { // read in data
                 mInBuffer.template readAdvance<CHANNELS>(
                         impulse, c.mHalfNumCoefs,
@@ -487,7 +490,7 @@
                 while (phaseFraction >= phaseWrapLimit) {
                     inputIndex++;
                     if (inputIndex >= mBuffer.frameCount) {
-                        inputIndex -= mBuffer.frameCount;
+                        inputIndex = 0;
                         provider->releaseBuffer(&mBuffer);
                         break;
                     }
@@ -535,15 +538,22 @@
 done:
         // often arrives here when input buffer runs out
         if (inputIndex >= frameCount) {
-            inputIndex -= frameCount;
+            inputIndex = 0;
             provider->releaseBuffer(&mBuffer);
-            // mBuffer.frameCount MUST be zero here.
+            ALOG_ASSERT(mBuffer.frameCount == 0);
         }
     }
 
 resample_exit:
+    // Release frames to avoid the count being inaccurate for pts timing.
+    // TODO: Avoid this extra check by making fetch count exact. This is tricky
+    // due to the overfetching mechanism which loads unnecessarily when
+    // mBuffer.frameCount == 0.
+    if (inputIndex) {
+        mBuffer.frameCount = inputIndex;
+        provider->releaseBuffer(&mBuffer);
+    }
     mInBuffer.setImpulse(impulse);
-    mInputIndex = inputIndex;
     mPhaseFraction = phaseFraction;
 }
 
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/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
new file mode 100644
index 0000000..0c9b976
--- /dev/null
+++ b/services/audioflinger/FastCapture.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "FastCapture"
+//#define LOG_NDEBUG 0
+
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
+#include "Configuration.h"
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#include <media/AudioBufferProvider.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include "FastCapture.h"
+
+namespace android {
+
+/*static*/ const FastCaptureState FastCapture::initial;
+
+FastCapture::FastCapture() : FastThread(),
+    inputSource(NULL), inputSourceGen(0), pipeSink(NULL), pipeSinkGen(0),
+    readBuffer(NULL), readBufferState(-1), format(Format_Invalid), sampleRate(0),
+    // dummyDumpState
+    totalNativeFramesRead(0)
+{
+    previous = &initial;
+    current = &initial;
+
+    mDummyDumpState = &dummyDumpState;
+}
+
+FastCapture::~FastCapture()
+{
+}
+
+FastCaptureStateQueue* FastCapture::sq()
+{
+    return &mSQ;
+}
+
+const FastThreadState *FastCapture::poll()
+{
+    return mSQ.poll();
+}
+
+void FastCapture::setLog(NBLog::Writer *logWriter __unused)
+{
+}
+
+void FastCapture::onIdle()
+{
+    preIdle = *(const FastCaptureState *)current;
+    current = &preIdle;
+}
+
+void FastCapture::onExit()
+{
+    delete[] readBuffer;
+}
+
+bool FastCapture::isSubClassCommand(FastThreadState::Command command)
+{
+    switch ((FastCaptureState::Command) command) {
+    case FastCaptureState::READ:
+    case FastCaptureState::WRITE:
+    case FastCaptureState::READ_WRITE:
+        return true;
+    default:
+        return false;
+    }
+}
+
+void FastCapture::onStateChange()
+{
+    const FastCaptureState * const current = (const FastCaptureState *) this->current;
+    const FastCaptureState * const previous = (const FastCaptureState *) this->previous;
+    FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState;
+    const size_t frameCount = current->mFrameCount;
+
+    bool eitherChanged = false;
+
+    // check for change in input HAL configuration
+    NBAIO_Format previousFormat = format;
+    if (current->mInputSourceGen != inputSourceGen) {
+        inputSource = current->mInputSource;
+        inputSourceGen = current->mInputSourceGen;
+        if (inputSource == NULL) {
+            format = Format_Invalid;
+            sampleRate = 0;
+        } else {
+            format = inputSource->format();
+            sampleRate = Format_sampleRate(format);
+            unsigned channelCount = Format_channelCount(format);
+            ALOG_ASSERT(channelCount == 1 || channelCount == 2);
+        }
+        dumpState->mSampleRate = sampleRate;
+        eitherChanged = true;
+    }
+
+    // check for change in pipe
+    if (current->mPipeSinkGen != pipeSinkGen) {
+        pipeSink = current->mPipeSink;
+        pipeSinkGen = current->mPipeSinkGen;
+        eitherChanged = true;
+    }
+
+    // input source and pipe sink must be compatible
+    if (eitherChanged && inputSource != NULL && pipeSink != NULL) {
+        ALOG_ASSERT(Format_isEqual(format, pipeSink->format()));
+    }
+
+    if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) {
+        // FIXME to avoid priority inversion, don't delete here
+        delete[] readBuffer;
+        readBuffer = NULL;
+        if (frameCount > 0 && sampleRate > 0) {
+            // FIXME new may block for unbounded time at internal mutex of the heap
+            //       implementation; it would be better to have normal capture thread allocate for
+            //       us to avoid blocking here and to prevent possible priority inversion
+            unsigned channelCount = Format_channelCount(format);
+            // FIXME frameSize
+            readBuffer = new short[frameCount * channelCount];
+            periodNs = (frameCount * 1000000000LL) / sampleRate;    // 1.00
+            underrunNs = (frameCount * 1750000000LL) / sampleRate;  // 1.75
+            overrunNs = (frameCount * 500000000LL) / sampleRate;    // 0.50
+            forceNs = (frameCount * 950000000LL) / sampleRate;      // 0.95
+            warmupNs = (frameCount * 500000000LL) / sampleRate;     // 0.50
+        } else {
+            periodNs = 0;
+            underrunNs = 0;
+            overrunNs = 0;
+            forceNs = 0;
+            warmupNs = 0;
+        }
+        readBufferState = -1;
+        dumpState->mFrameCount = frameCount;
+    }
+
+}
+
+void FastCapture::onWork()
+{
+    const FastCaptureState * const current = (const FastCaptureState *) this->current;
+    FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState;
+    const FastCaptureState::Command command = this->command;
+    const size_t frameCount = current->mFrameCount;
+
+    if ((command & FastCaptureState::READ) /*&& isWarm*/) {
+        ALOG_ASSERT(inputSource != NULL);
+        ALOG_ASSERT(readBuffer != NULL);
+        dumpState->mReadSequence++;
+        ATRACE_BEGIN("read");
+        ssize_t framesRead = inputSource->read(readBuffer, frameCount,
+                AudioBufferProvider::kInvalidPTS);
+        ATRACE_END();
+        dumpState->mReadSequence++;
+        if (framesRead >= 0) {
+            LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount);
+            totalNativeFramesRead += framesRead;
+            dumpState->mFramesRead = totalNativeFramesRead;
+            readBufferState = framesRead;
+        } else {
+            dumpState->mReadErrors++;
+            readBufferState = 0;
+        }
+        // FIXME rename to attemptedIO
+        attemptedWrite = true;
+    }
+
+    if (command & FastCaptureState::WRITE) {
+        ALOG_ASSERT(pipeSink != NULL);
+        ALOG_ASSERT(readBuffer != NULL);
+        if (readBufferState < 0) {
+            unsigned channelCount = Format_channelCount(format);
+            // FIXME frameSize
+            memset(readBuffer, 0, frameCount * channelCount * sizeof(short));
+            readBufferState = frameCount;
+        }
+        if (readBufferState > 0) {
+            ssize_t framesWritten = pipeSink->write(readBuffer, readBufferState);
+            // FIXME This supports at most one fast capture client.
+            //       To handle multiple clients this could be converted to an array,
+            //       or with a lot more work the control block could be shared by all clients.
+            audio_track_cblk_t* cblk = current->mCblk;
+            if (cblk != NULL && framesWritten > 0) {
+                int32_t rear = cblk->u.mStreaming.mRear;
+                android_atomic_release_store(framesWritten + rear, &cblk->u.mStreaming.mRear);
+                cblk->mServer += framesWritten;
+                int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
+                if (!(old & CBLK_FUTEX_WAKE)) {
+                    // client is never in server process, so don't use FUTEX_WAKE_PRIVATE
+                    (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, 1);
+                }
+            }
+        }
+    }
+}
+
+FastCaptureDumpState::FastCaptureDumpState() : FastThreadDumpState(),
+    mReadSequence(0), mFramesRead(0), mReadErrors(0), mSampleRate(0), mFrameCount(0)
+{
+}
+
+FastCaptureDumpState::~FastCaptureDumpState()
+{
+}
+
+}   // namespace android
diff --git a/services/audioflinger/FastCapture.h b/services/audioflinger/FastCapture.h
new file mode 100644
index 0000000..e535b9d
--- /dev/null
+++ b/services/audioflinger/FastCapture.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 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 ANDROID_AUDIO_FAST_CAPTURE_H
+#define ANDROID_AUDIO_FAST_CAPTURE_H
+
+#include "FastThread.h"
+#include "StateQueue.h"
+#include "FastCaptureState.h"
+
+namespace android {
+
+typedef StateQueue<FastCaptureState> FastCaptureStateQueue;
+
+struct FastCaptureDumpState : FastThreadDumpState {
+    FastCaptureDumpState();
+    /*virtual*/ ~FastCaptureDumpState();
+
+    // FIXME by renaming, could pull up many of these to FastThreadDumpState
+    uint32_t mReadSequence;     // incremented before and after each read()
+    uint32_t mFramesRead;       // total number of frames read successfully
+    uint32_t mReadErrors;       // total number of read() errors
+    uint32_t mSampleRate;
+    size_t   mFrameCount;
+};
+
+class FastCapture : public FastThread {
+
+public:
+            FastCapture();
+    virtual ~FastCapture();
+
+            FastCaptureStateQueue*  sq();
+
+private:
+            FastCaptureStateQueue   mSQ;
+
+    // callouts
+    virtual const FastThreadState *poll();
+    virtual void setLog(NBLog::Writer *logWriter);
+    virtual void onIdle();
+    virtual void onExit();
+    virtual bool isSubClassCommand(FastThreadState::Command command);
+    virtual void onStateChange();
+    virtual void onWork();
+
+    static const FastCaptureState initial;
+    FastCaptureState preIdle; // copy of state before we went into idle
+    // FIXME by renaming, could pull up many of these to FastThread
+    NBAIO_Source *inputSource;
+    int inputSourceGen;
+    NBAIO_Sink *pipeSink;
+    int pipeSinkGen;
+    short *readBuffer;
+    ssize_t readBufferState;    // number of initialized frames in readBuffer, or -1 to clear
+    NBAIO_Format format;
+    unsigned sampleRate;
+    FastCaptureDumpState dummyDumpState;
+    uint32_t totalNativeFramesRead; // copied to dumpState->mFramesRead
+
+};  // class FastCapture
+
+}   // namespace android
+
+#endif  // ANDROID_AUDIO_FAST_CAPTURE_H
diff --git a/services/audioflinger/FastCaptureState.cpp b/services/audioflinger/FastCaptureState.cpp
new file mode 100644
index 0000000..1d029b7
--- /dev/null
+++ b/services/audioflinger/FastCaptureState.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 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 "FastCaptureState.h"
+
+namespace android {
+
+FastCaptureState::FastCaptureState() : FastThreadState(),
+    mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0), mFrameCount(0)
+{
+}
+
+FastCaptureState::~FastCaptureState()
+{
+}
+
+}   // android
diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h
new file mode 100644
index 0000000..29c865a
--- /dev/null
+++ b/services/audioflinger/FastCaptureState.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 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 ANDROID_AUDIO_FAST_CAPTURE_STATE_H
+#define ANDROID_AUDIO_FAST_CAPTURE_STATE_H
+
+#include <media/nbaio/NBAIO.h>
+#include "FastThreadState.h"
+#include <private/media/AudioTrackShared.h>
+
+namespace android {
+
+// Represent a single state of the fast capture
+struct FastCaptureState : FastThreadState {
+                FastCaptureState();
+    /*virtual*/ ~FastCaptureState();
+
+    // all pointer fields use raw pointers; objects are owned and ref-counted by RecordThread
+    NBAIO_Source    *mInputSource;      // HAL input device, must already be negotiated
+    // FIXME by renaming, could pull up these fields to FastThreadState
+    int             mInputSourceGen;    // increment when mInputSource is assigned
+    NBAIO_Sink      *mPipeSink;         // after reading from input source, write to this pipe sink
+    int             mPipeSinkGen;       // increment when mPipeSink is assigned
+    size_t          mFrameCount;        // number of frames per fast capture buffer
+    audio_track_cblk_t  *mCblk;         // control block for the single fast client, or NULL
+
+    // Extends FastThreadState::Command
+    static const Command
+        // The following commands also process configuration changes, and can be "or"ed:
+        READ = 0x8,             // read from input source
+        WRITE = 0x10,           // write to pipe sink
+        READ_WRITE = 0x18;      // read from input source and write to pipe sink
+
+};  // struct FastCaptureState
+
+}   // namespace android
+
+#endif  // ANDROID_AUDIO_FAST_CAPTURE_STATE_H
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 5cb42cc..13b21ec 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>
@@ -37,6 +36,7 @@
 #include <cpustats/ThreadCpuUsage.h>
 #endif
 #endif
+#include <audio_utils/format.h>
 #include "AudioMixer.h"
 #include "FastMixer.h"
 
@@ -53,8 +53,12 @@
     outputSink(NULL),
     outputSinkGen(0),
     mixer(NULL),
-    mixBuffer(NULL),
-    mixBufferState(UNDEFINED),
+    mSinkBuffer(NULL),
+    mSinkBufferSize(0),
+    mMixerBuffer(NULL),
+    mMixerBufferSize(0),
+    mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT),
+    mMixerBufferState(UNDEFINED),
     format(Format_Invalid),
     sampleRate(0),
     fastTracksGen(0),
@@ -109,7 +113,8 @@
 void FastMixer::onExit()
 {
     delete mixer;
-    delete[] mixBuffer;
+    free(mMixerBuffer);
+    free(mSinkBuffer);
 }
 
 bool FastMixer::isSubClassCommand(FastThreadState::Command command)
@@ -155,14 +160,23 @@
         // FIXME to avoid priority inversion, don't delete here
         delete mixer;
         mixer = NULL;
-        delete[] mixBuffer;
-        mixBuffer = NULL;
+        free(mMixerBuffer);
+        mMixerBuffer = NULL;
+        free(mSinkBuffer);
+        mSinkBuffer = NULL;
         if (frameCount > 0 && sampleRate > 0) {
             // FIXME new may block for unbounded time at internal mutex of the heap
             //       implementation; it would be better to have normal mixer allocate for us
             //       to avoid blocking here and to prevent possible priority inversion
             mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks);
-            mixBuffer = new short[frameCount * FCC_2];
+            const size_t mixerFrameSize = FCC_2 * audio_bytes_per_sample(mMixerBufferFormat);
+            mMixerBufferSize = mixerFrameSize * frameCount;
+            (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize);
+            const size_t sinkFrameSize = FCC_2 * audio_bytes_per_sample(format.mFormat);
+            if (sinkFrameSize > mixerFrameSize) { // need a sink buffer
+                mSinkBufferSize = sinkFrameSize * frameCount;
+                (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize);
+            }
             periodNs = (frameCount * 1000000000LL) / sampleRate;    // 1.00
             underrunNs = (frameCount * 1750000000LL) / sampleRate;  // 1.75
             overrunNs = (frameCount * 500000000LL) / sampleRate;    // 0.50
@@ -175,7 +189,7 @@
             forceNs = 0;
             warmupNs = 0;
         }
-        mixBufferState = UNDEFINED;
+        mMixerBufferState = UNDEFINED;
 #if !LOG_NDEBUG
         for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) {
             fastTrackNames[i] = -1;
@@ -193,7 +207,7 @@
     const unsigned currentTrackMask = current->mTrackMask;
     dumpState->mTrackMask = currentTrackMask;
     if (current->mFastTracksGen != fastTracksGen) {
-        ALOG_ASSERT(mixBuffer != NULL);
+        ALOG_ASSERT(mMixerBuffer != NULL);
         int name;
 
         // process removed tracks first to avoid running out of track names
@@ -224,17 +238,20 @@
             AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
             ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1);
             if (mixer != NULL) {
-                // calling getTrackName with default channel mask and a random invalid
-                //   sessionId (no effects here)
-                name = mixer->getTrackName(AUDIO_CHANNEL_OUT_STEREO, -555);
+                name = mixer->getTrackName(fastTrack->mChannelMask,
+                        fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
                 ALOG_ASSERT(name >= 0);
                 fastTrackNames[i] = name;
                 mixer->setBufferProvider(name, bufferProvider);
                 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
-                        (void *) mixBuffer);
+                        (void *) mMixerBuffer);
                 // newly allocated track names default to full scale volume
-                mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
-                        (void *)(uintptr_t)fastTrack->mChannelMask);
+                mixer->setParameter(
+                        name,
+                        AudioMixer::TRACK,
+                        AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
+                mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
+                        (void *)(uintptr_t)fastTrack->mFormat);
                 mixer->enable(name);
             }
             generations[i] = fastTrack->mGeneration;
@@ -257,12 +274,18 @@
                     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);
+                    mixer->setParameter(
+                            name,
+                            AudioMixer::TRACK,
+                            AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
+                    mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
+                            (void *)(uintptr_t)fastTrack->mFormat);
                     mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
                             (void *)(uintptr_t) fastTrack->mChannelMask);
                     // already enabled
@@ -285,7 +308,7 @@
     const size_t frameCount = current->mFrameCount;
 
     if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) {
-        ALOG_ASSERT(mixBuffer != NULL);
+        ALOG_ASSERT(mMixerBuffer != NULL);
         // for each track, update volume and check for underrun
         unsigned currentTrackMask = current->mTrackMask;
         while (currentTrackMask != 0) {
@@ -312,11 +335,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
@@ -360,26 +385,31 @@
 
         // process() is CPU-bound
         mixer->process(pts);
-        mixBufferState = MIXED;
-    } else if (mixBufferState == MIXED) {
-        mixBufferState = UNDEFINED;
+        mMixerBufferState = MIXED;
+    } else if (mMixerBufferState == MIXED) {
+        mMixerBufferState = UNDEFINED;
     }
     //bool didFullWrite = false;    // dumpsys could display a count of partial writes
-    if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mixBuffer != NULL)) {
-        if (mixBufferState == UNDEFINED) {
-            memset(mixBuffer, 0, frameCount * FCC_2 * sizeof(short));
-            mixBufferState = ZEROED;
+    if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) {
+        if (mMixerBufferState == UNDEFINED) {
+            memset(mMixerBuffer, 0, mMixerBufferSize);
+            mMixerBufferState = ZEROED;
+        }
+        void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer;
+        if (format.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format
+            memcpy_by_audio_format(buffer, format.mFormat, mMixerBuffer, mMixerBufferFormat,
+                    frameCount * Format_channelCount(format));
         }
         // if non-NULL, then duplicate write() to this non-blocking sink
         NBAIO_Sink* teeSink;
         if ((teeSink = current->mTeeSink) != NULL) {
-            (void) teeSink->write(mixBuffer, frameCount);
+            (void) teeSink->write(mMixerBuffer, frameCount);
         }
         // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink,
         //       but this code should be modified to handle both non-blocking and blocking sinks
         dumpState->mWriteSequence++;
         ATRACE_BEGIN("write");
-        ssize_t framesWritten = outputSink->write(mixBuffer, frameCount);
+        ssize_t framesWritten = outputSink->write(buffer, frameCount);
         ATRACE_END();
         dumpState->mWriteSequence++;
         if (framesWritten >= 0) {
@@ -463,7 +493,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
@@ -497,10 +527,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,
@@ -552,26 +582,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);
@@ -582,12 +612,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
@@ -597,9 +627,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];
@@ -619,7 +649,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..4671670 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"
@@ -63,8 +61,16 @@
     NBAIO_Sink *outputSink;
     int outputSinkGen;
     AudioMixer* mixer;
-    short *mixBuffer;
-    enum {UNDEFINED, MIXED, ZEROED} mixBufferState;
+
+    // mSinkBuffer audio format is stored in format.mFormat.
+    void* mSinkBuffer;                  // used for mixer output format translation
+                                        // if sink format is different than mixer output.
+    size_t mSinkBufferSize;
+    void* mMixerBuffer;                 // mixer output buffer.
+    size_t mMixerBufferSize;
+    audio_format_t mMixerBufferFormat;  // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT).
+
+    enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState;
     NBAIO_Format format;
     unsigned sampleRate;
     int fastTracksGen;
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index 8e6d0d4..3aa8dad 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -20,7 +20,7 @@
 
 FastTrack::FastTrack() :
     mBufferProvider(NULL), mVolumeProvider(NULL),
-    mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mGeneration(0)
+    mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mFormat(AUDIO_FORMAT_INVALID), mGeneration(0)
 {
 }
 
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index be1a376..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() { }
@@ -45,6 +45,7 @@
     ExtendedAudioBufferProvider* mBufferProvider; // must be NULL if inactive, or non-NULL if active
     VolumeProvider*         mVolumeProvider; // optional; if NULL then full-scale
     audio_channel_mask_t    mChannelMask;    // AUDIO_CHANNEL_OUT_MONO or AUDIO_CHANNEL_OUT_STEREO
+    audio_format_t          mFormat;         // track format
     int                     mGeneration;     // increment when any field is assigned
 };
 
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/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
new file mode 100644
index 0000000..96a8127
--- /dev/null
+++ b/services/audioflinger/PatchPanel.cpp
@@ -0,0 +1,441 @@
+/*
+**
+** 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.
+*/
+
+
+#define LOG_TAG "AudioFlinger::PatchPanel"
+//#define LOG_NDEBUG 0
+
+#include "Configuration.h"
+#include <utils/Log.h>
+#include <audio_utils/primitives.h>
+
+#include "AudioFlinger.h"
+#include "ServiceUtilities.h"
+#include <media/AudioParameter.h>
+
+// ----------------------------------------------------------------------------
+
+// Note: the following macro is used for extremely verbose logging message.  In
+// order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG set to
+// 0; but one side effect of this is to turn all LOGV's as well.  Some messages
+// are so verbose that we want to suppress them even when we have ALOG_ASSERT
+// turned on.  Do not uncomment the #def below unless you really know what you
+// are doing and want to see all of the extremely verbose messages.
+//#define VERY_VERY_VERBOSE_LOGGING
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(a...) do { } while(0)
+#endif
+
+namespace android {
+
+/* List connected audio ports and their attributes */
+status_t AudioFlinger::listAudioPorts(unsigned int *num_ports,
+                                struct audio_port *ports)
+{
+    Mutex::Autolock _l(mLock);
+    if (mPatchPanel != 0) {
+        return mPatchPanel->listAudioPorts(num_ports, ports);
+    }
+    return NO_INIT;
+}
+
+/* Get supported attributes for a given audio port */
+status_t AudioFlinger::getAudioPort(struct audio_port *port)
+{
+    Mutex::Autolock _l(mLock);
+    if (mPatchPanel != 0) {
+        return mPatchPanel->getAudioPort(port);
+    }
+    return NO_INIT;
+}
+
+
+/* Connect a patch between several source and sink ports */
+status_t AudioFlinger::createAudioPatch(const struct audio_patch *patch,
+                                   audio_patch_handle_t *handle)
+{
+    Mutex::Autolock _l(mLock);
+    if (mPatchPanel != 0) {
+        return mPatchPanel->createAudioPatch(patch, handle);
+    }
+    return NO_INIT;
+}
+
+/* Disconnect a patch */
+status_t AudioFlinger::releaseAudioPatch(audio_patch_handle_t handle)
+{
+    Mutex::Autolock _l(mLock);
+    if (mPatchPanel != 0) {
+        return mPatchPanel->releaseAudioPatch(handle);
+    }
+    return NO_INIT;
+}
+
+
+/* List connected audio ports and they attributes */
+status_t AudioFlinger::listAudioPatches(unsigned int *num_patches,
+                                  struct audio_patch *patches)
+{
+    Mutex::Autolock _l(mLock);
+    if (mPatchPanel != 0) {
+        return mPatchPanel->listAudioPatches(num_patches, patches);
+    }
+    return NO_INIT;
+}
+
+/* Set audio port configuration */
+status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config)
+{
+    Mutex::Autolock _l(mLock);
+    if (mPatchPanel != 0) {
+        return mPatchPanel->setAudioPortConfig(config);
+    }
+    return NO_INIT;
+}
+
+
+AudioFlinger::PatchPanel::PatchPanel(const sp<AudioFlinger>& audioFlinger)
+                                   : mAudioFlinger(audioFlinger)
+{
+}
+
+AudioFlinger::PatchPanel::~PatchPanel()
+{
+}
+
+/* List connected audio ports and their attributes */
+status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused,
+                                struct audio_port *ports __unused)
+{
+    ALOGV("listAudioPorts");
+    return NO_ERROR;
+}
+
+/* Get supported attributes for a given audio port */
+status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port *port __unused)
+{
+    ALOGV("getAudioPort");
+    return NO_ERROR;
+}
+
+
+/* Connect a patch between several source and sink ports */
+status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *patch,
+                                   audio_patch_handle_t *handle)
+{
+    ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d",
+          patch->num_sources, patch->num_sinks, *handle);
+    status_t status = NO_ERROR;
+
+    audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE;
+
+    sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
+    if (audioflinger == 0) {
+        return NO_INIT;
+    }
+    if (handle == NULL || patch == NULL) {
+        return BAD_VALUE;
+    }
+    // limit number of sources to 1 for now
+    if (patch->num_sources == 0 || patch->num_sources > 1 ||
+            patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
+        return BAD_VALUE;
+    }
+
+    for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) {
+        if (*handle == mPatches[index]->mHandle) {
+            ALOGV("createAudioPatch() removing patch handle %d", *handle);
+            halHandle = mPatches[index]->mHalHandle;
+            mPatches.removeAt(index);
+            break;
+        }
+    }
+
+    switch (patch->sources[0].type) {
+        case AUDIO_PORT_TYPE_DEVICE: {
+            // limit number of sinks to 1 for now
+            if (patch->num_sinks > 1) {
+                return BAD_VALUE;
+            }
+            audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module;
+            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+            if (index < 0) {
+                ALOGW("createAudioPatch() bad src hw module %d", src_module);
+                return BAD_VALUE;
+            }
+            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+            for (unsigned int i = 0; i < patch->num_sinks; i++) {
+                // reject connection to different sink types
+                if (patch->sinks[i].type != patch->sinks[0].type) {
+                    ALOGW("createAudioPatch() different sink types in same patch not supported");
+                    return BAD_VALUE;
+                }
+                // limit to connections between sinks and sources on same HW module
+                if (patch->sinks[i].ext.mix.hw_module != src_module) {
+                    ALOGW("createAudioPatch() cannot connect source on module %d to"
+                            "sink on module %d", src_module, patch->sinks[i].ext.mix.hw_module);
+                    return BAD_VALUE;
+                }
+
+                // limit to connections between devices and output streams for HAL before 3.0
+                if ((audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) &&
+                        (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX)) {
+                    ALOGW("createAudioPatch() invalid sink type %d for device source",
+                          patch->sinks[i].type);
+                    return BAD_VALUE;
+                }
+            }
+
+            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+                if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+                    sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
+                                                                    patch->sinks[0].ext.mix.handle);
+                    if (thread == 0) {
+                        ALOGW("createAudioPatch() bad capture I/O handle %d",
+                                                                  patch->sinks[0].ext.mix.handle);
+                        return BAD_VALUE;
+                    }
+                    status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
+                } else {
+                    audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+                    status = hwDevice->create_audio_patch(hwDevice,
+                                                           patch->num_sources,
+                                                           patch->sources,
+                                                           patch->num_sinks,
+                                                           patch->sinks,
+                                                           &halHandle);
+                }
+            } else {
+                sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
+                                                                    patch->sinks[0].ext.mix.handle);
+                if (thread == 0) {
+                    ALOGW("createAudioPatch() bad capture I/O handle %d",
+                                                                  patch->sinks[0].ext.mix.handle);
+                    return BAD_VALUE;
+                }
+                AudioParameter param;
+                param.addInt(String8(AudioParameter::keyRouting),
+                             (int)patch->sources[0].ext.device.type);
+                param.addInt(String8(AudioParameter::keyInputSource),
+                                                     (int)patch->sinks[0].ext.mix.usecase.source);
+
+                ALOGW("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
+                                                                      param.toString().string());
+                status = thread->setParameters(param.toString());
+            }
+        } break;
+        case AUDIO_PORT_TYPE_MIX: {
+            audio_module_handle_t src_module =  patch->sources[0].ext.mix.hw_module;
+            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+            if (index < 0) {
+                ALOGW("createAudioPatch() bad src hw module %d", src_module);
+                return BAD_VALUE;
+            }
+            // limit to connections between devices and output streams
+            for (unsigned int i = 0; i < patch->num_sinks; i++) {
+                if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
+                    ALOGW("createAudioPatch() invalid sink type %d for bus source",
+                          patch->sinks[i].type);
+                    return BAD_VALUE;
+                }
+                // limit to connections between sinks and sources on same HW module
+                if (patch->sinks[i].ext.device.hw_module != src_module) {
+                    return BAD_VALUE;
+                }
+            }
+            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+            sp<ThreadBase> thread =
+                            audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
+            if (thread == 0) {
+                ALOGW("createAudioPatch() bad playback I/O handle %d",
+                          patch->sources[0].ext.mix.handle);
+                return BAD_VALUE;
+            }
+            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+                status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
+            } else {
+                audio_devices_t type = AUDIO_DEVICE_NONE;
+                for (unsigned int i = 0; i < patch->num_sinks; i++) {
+                    type |= patch->sinks[i].ext.device.type;
+                }
+                AudioParameter param;
+                param.addInt(String8(AudioParameter::keyRouting), (int)type);
+                status = thread->setParameters(param.toString());
+            }
+
+        } break;
+        default:
+            return BAD_VALUE;
+    }
+    ALOGV("createAudioPatch() status %d", status);
+    if (status == NO_ERROR) {
+        *handle = audioflinger->nextUniqueId();
+        Patch *newPatch = new Patch(patch);
+        newPatch->mHandle = *handle;
+        newPatch->mHalHandle = halHandle;
+        mPatches.add(newPatch);
+        ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle);
+    }
+    return status;
+}
+
+/* Disconnect a patch */
+status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle)
+{
+    ALOGV("releaseAudioPatch handle %d", handle);
+    status_t status = NO_ERROR;
+    size_t index;
+
+    sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
+    if (audioflinger == 0) {
+        return NO_INIT;
+    }
+
+    for (index = 0; index < mPatches.size(); index++) {
+        if (handle == mPatches[index]->mHandle) {
+            break;
+        }
+    }
+    if (index == mPatches.size()) {
+        return BAD_VALUE;
+    }
+
+    struct audio_patch *patch = &mPatches[index]->mAudioPatch;
+
+    switch (patch->sources[0].type) {
+        case AUDIO_PORT_TYPE_DEVICE: {
+            audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module;
+            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+            if (index < 0) {
+                ALOGW("releaseAudioPatch() bad src hw module %d", src_module);
+                status = BAD_VALUE;
+                break;
+            }
+            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+                if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+                    sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
+                                                                    patch->sinks[0].ext.mix.handle);
+                    if (thread == 0) {
+                        ALOGW("createAudioPatch() bad capture I/O handle %d",
+                                                                  patch->sinks[0].ext.mix.handle);
+                        status = BAD_VALUE;
+                        break;
+                    }
+                    status = thread->sendReleaseAudioPatchConfigEvent(mPatches[index]->mHalHandle);
+                } else {
+                    audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+                    status = hwDevice->release_audio_patch(hwDevice, mPatches[index]->mHalHandle);
+                }
+            } else {
+                sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
+                                                                    patch->sinks[0].ext.mix.handle);
+                if (thread == 0) {
+                    ALOGW("releaseAudioPatch() bad capture I/O handle %d",
+                                                                  patch->sinks[0].ext.mix.handle);
+                    status = BAD_VALUE;
+                    break;
+                }
+                AudioParameter param;
+                param.addInt(String8(AudioParameter::keyRouting), 0);
+                ALOGW("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
+                                                                      param.toString().string());
+                status = thread->setParameters(param.toString());
+            }
+        } break;
+        case AUDIO_PORT_TYPE_MIX: {
+            audio_module_handle_t src_module =  patch->sources[0].ext.mix.hw_module;
+            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+            if (index < 0) {
+                ALOGW("releaseAudioPatch() bad src hw module %d", src_module);
+                status = BAD_VALUE;
+                break;
+            }
+            sp<ThreadBase> thread =
+                            audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
+            if (thread == 0) {
+                ALOGW("releaseAudioPatch() bad playback I/O handle %d",
+                                                              patch->sources[0].ext.mix.handle);
+                status = BAD_VALUE;
+                break;
+            }
+            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+            if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+                status = thread->sendReleaseAudioPatchConfigEvent(mPatches[index]->mHalHandle);
+            } else {
+                AudioParameter param;
+                param.addInt(String8(AudioParameter::keyRouting), (int)0);
+                status = thread->setParameters(param.toString());
+            }
+        } break;
+        default:
+            status = BAD_VALUE;
+            break;
+    }
+
+    delete (mPatches[index]);
+    mPatches.removeAt(index);
+    return status;
+}
+
+
+/* List connected audio ports and they attributes */
+status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused,
+                                  struct audio_patch *patches __unused)
+{
+    ALOGV("listAudioPatches");
+    return NO_ERROR;
+}
+
+/* Set audio port configuration */
+status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config)
+{
+    ALOGV("setAudioPortConfig");
+    status_t status = NO_ERROR;
+
+    sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
+    if (audioflinger == 0) {
+        return NO_INIT;
+    }
+
+    audio_module_handle_t module;
+    if (config->type == AUDIO_PORT_TYPE_DEVICE) {
+        module = config->ext.device.hw_module;
+    } else {
+        module = config->ext.mix.hw_module;
+    }
+
+    ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module);
+    if (index < 0) {
+        ALOGW("setAudioPortConfig() bad hw module %d", module);
+        return BAD_VALUE;
+    }
+
+    AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+    if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+        audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+        return hwDevice->set_audio_port_config(hwDevice, config);
+    } else {
+        return INVALID_OPERATION;
+    }
+    return NO_ERROR;
+}
+
+
+}; // namespace android
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
new file mode 100644
index 0000000..7f78621
--- /dev/null
+++ b/services/audioflinger/PatchPanel.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 INCLUDING_FROM_AUDIOFLINGER_H
+    #error This header file should only be included from AudioFlinger.h
+#endif
+
+class PatchPanel : public RefBase {
+public:
+    PatchPanel(const sp<AudioFlinger>& audioFlinger);
+    virtual ~PatchPanel();
+
+    /* List connected audio ports and their attributes */
+    status_t listAudioPorts(unsigned int *num_ports,
+                                    struct audio_port *ports);
+
+    /* Get supported attributes for a given audio port */
+    status_t getAudioPort(struct audio_port *port);
+
+    /* Create a patch between several source and sink ports */
+    status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle);
+
+    /* Release a patch */
+    status_t releaseAudioPatch(audio_patch_handle_t handle);
+
+    /* List connected audio devices and they attributes */
+    status_t listAudioPatches(unsigned int *num_patches,
+                                      struct audio_patch *patches);
+
+    /* Set audio port configuration */
+    status_t setAudioPortConfig(const struct audio_port_config *config);
+
+    class Patch {
+    public:
+        Patch(const struct audio_patch *patch) :
+            mAudioPatch(*patch), mHandle(0), mHalHandle(0) {}
+
+        struct audio_patch mAudioPatch;
+        audio_patch_handle_t mHandle;
+        audio_patch_handle_t mHalHandle;
+    };
+private:
+    const wp<AudioFlinger>  mAudioFlinger;
+    SortedVector <Patch *> mPatches;
+};
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/StateQueueInstantiations.cpp b/services/audioflinger/StateQueueInstantiations.cpp
index 0d5cd0c..6f4505e 100644
--- a/services/audioflinger/StateQueueInstantiations.cpp
+++ b/services/audioflinger/StateQueueInstantiations.cpp
@@ -16,12 +16,14 @@
 
 #include "Configuration.h"
 #include "FastMixerState.h"
+#include "FastCaptureState.h"
 #include "StateQueue.h"
 
 // FIXME hack for gcc
 
 namespace android {
 
-template class StateQueue<FastMixerState>;  // typedef FastMixerStateQueue
+template class StateQueue<FastMixerState>;      // typedef FastMixerStateQueue
+template class StateQueue<FastCaptureState>;    // typedef FastCaptureStateQueue
 
 }
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 2d4e025..742163b 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>
@@ -141,8 +142,17 @@
 // FIXME It would be better for client to tell AudioFlinger the value of N,
 // so AudioFlinger could allocate the right amount of memory.
 // See the client's minBufCount and mNotificationFramesAct calculations for details.
+
+// This is the default value, if not specified by property.
 static const int kFastTrackMultiplier = 2;
 
+// The minimum and maximum allowed values
+static const int kFastTrackMultiplierMin = 1;
+static const int kFastTrackMultiplierMax = 2;
+
+// The actual value to use, which can be specified per-device via property af.fast_track_multiplier.
+static int sFastTrackMultiplier = kFastTrackMultiplier;
+
 // See Thread::readOnlyHeap().
 // Initially this heap is used to allocate client buffers for "fast" AudioRecord.
 // Eventually it will be the single buffer that FastCapture writes into via HAL read(),
@@ -151,6 +161,22 @@
 
 // ----------------------------------------------------------------------------
 
+static pthread_once_t sFastTrackMultiplierOnce = PTHREAD_ONCE_INIT;
+
+static void sFastTrackMultiplierInit()
+{
+    char value[PROPERTY_VALUE_MAX];
+    if (property_get("af.fast_track_multiplier", value, NULL) > 0) {
+        char *endptr;
+        unsigned long ul = strtoul(value, &endptr, 0);
+        if (*endptr == '\0' && kFastTrackMultiplierMin <= ul && ul <= kFastTrackMultiplierMax) {
+            sFastTrackMultiplier = (int) ul;
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+
 #ifdef ADD_BATTERY_DATA
 // To collect the amplifier usage
 static void addBatteryData(uint32_t params) {
@@ -400,6 +426,30 @@
     return sendConfigEvent_l(configEvent);
 }
 
+status_t AudioFlinger::ThreadBase::sendCreateAudioPatchConfigEvent(
+                                                        const struct audio_patch *patch,
+                                                        audio_patch_handle_t *handle)
+{
+    Mutex::Autolock _l(mLock);
+    sp<ConfigEvent> configEvent = (ConfigEvent *)new CreateAudioPatchConfigEvent(*patch, *handle);
+    status_t status = sendConfigEvent_l(configEvent);
+    if (status == NO_ERROR) {
+        CreateAudioPatchConfigEventData *data =
+                                        (CreateAudioPatchConfigEventData *)configEvent->mData.get();
+        *handle = data->mHandle;
+    }
+    return status;
+}
+
+status_t AudioFlinger::ThreadBase::sendReleaseAudioPatchConfigEvent(
+                                                                const audio_patch_handle_t handle)
+{
+    Mutex::Autolock _l(mLock);
+    sp<ConfigEvent> configEvent = (ConfigEvent *)new ReleaseAudioPatchConfigEvent(handle);
+    return sendConfigEvent_l(configEvent);
+}
+
+
 // post condition: mConfigEvents.isEmpty()
 void AudioFlinger::ThreadBase::processConfigEvents_l()
 {
@@ -430,6 +480,16 @@
                 configChanged = true;
             }
         } break;
+        case CFG_EVENT_CREATE_AUDIO_PATCH: {
+            CreateAudioPatchConfigEventData *data =
+                                            (CreateAudioPatchConfigEventData *)event->mData.get();
+            event->mStatus = createAudioPatch_l(&data->mPatch, &data->mHandle);
+        } break;
+        case CFG_EVENT_RELEASE_AUDIO_PATCH: {
+            ReleaseAudioPatchConfigEventData *data =
+                                            (ReleaseAudioPatchConfigEventData *)event->mData.get();
+            event->mStatus = releaseAudioPatch_l(data->mHandle);
+        } break;
         default:
             ALOG_ASSERT(false, "processConfigEvents_l() unknown event type %d", event->mType);
             break;
@@ -504,30 +564,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 +1250,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 +1290,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);
 }
@@ -1321,7 +1380,12 @@
         ) {
         // if frameCount not specified, then it defaults to fast mixer (HAL) frame count
         if (frameCount == 0) {
-            frameCount = mFrameCount * kFastTrackMultiplier;
+            // read the fast track multiplier property the first time it is needed
+            int ok = pthread_once(&sFastTrackMultiplierOnce, sFastTrackMultiplierInit);
+            if (ok != 0) {
+                ALOGE("%s pthread_once failed: %d", __func__, ok);
+            }
+            frameCount = mFrameCount * sFastTrackMultiplier;
         }
         ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
                 frameCount, mFrameCount);
@@ -1730,7 +1794,7 @@
         LOG_ALWAYS_FATAL("HAL channel mask %#x not supported for mixed output; "
                 "must be AUDIO_CHANNEL_OUT_STEREO", mChannelMask);
     }
-    mChannelCount = popcount(mChannelMask);
+    mChannelCount = audio_channel_count_from_out_mask(mChannelMask);
     mFormat = mOutput->stream->common.get_format(&mOutput->stream->common);
     if (!audio_is_valid_format(mFormat)) {
         LOG_ALWAYS_FATAL("HAL format %#x not valid for output", mFormat);
@@ -2593,6 +2657,47 @@
     }
     return INVALID_OPERATION;
 }
+
+status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_patch *patch,
+                                                          audio_patch_handle_t *handle)
+{
+    status_t status = NO_ERROR;
+    if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+        // store new device and send to effects
+        audio_devices_t type = AUDIO_DEVICE_NONE;
+        for (unsigned int i = 0; i < patch->num_sinks; i++) {
+            type |= patch->sinks[i].ext.device.type;
+        }
+        mOutDevice = type;
+        for (size_t i = 0; i < mEffectChains.size(); i++) {
+            mEffectChains[i]->setDevice_l(mOutDevice);
+        }
+
+        audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice();
+        status = hwDevice->create_audio_patch(hwDevice,
+                                               patch->num_sources,
+                                               patch->sources,
+                                               patch->num_sinks,
+                                               patch->sinks,
+                                               handle);
+    } else {
+        ALOG_ASSERT(false, "createAudioPatch_l() called on a pre 3.0 HAL");
+    }
+    return status;
+}
+
+status_t AudioFlinger::PlaybackThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
+{
+    status_t status = NO_ERROR;
+    if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+        audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice();
+        status = hwDevice->release_audio_patch(hwDevice, handle);
+    } else {
+        ALOG_ASSERT(false, "releaseAudioPatch_l() called on a pre 3.0 HAL");
+    }
+    return status;
+}
+
 // ----------------------------------------------------------------------------
 
 AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
@@ -2639,9 +2744,27 @@
         break;
     }
     if (initFastMixer) {
+        audio_format_t fastMixerFormat;
+        if (mMixerBufferEnabled && mEffectBufferEnabled) {
+            fastMixerFormat = AUDIO_FORMAT_PCM_FLOAT;
+        } else {
+            fastMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
+        }
+        if (mFormat != fastMixerFormat) {
+            // change our Sink format to accept our intermediate precision
+            mFormat = fastMixerFormat;
+            free(mSinkBuffer);
+            mFrameSize = mChannelCount * audio_bytes_per_sample(mFormat);
+            const size_t sinkBufferSize = mNormalFrameCount * mFrameSize;
+            (void)posix_memalign(&mSinkBuffer, 32, sinkBufferSize);
+        }
 
         // create a MonoPipe to connect our submix to FastMixer
         NBAIO_Format format = mOutputSink->format();
+        // adjust format to match that of the Fast Mixer
+        format.mFormat = fastMixerFormat;
+        format.mFrameSize = audio_bytes_per_sample(format.mFormat) * format.mChannelCount;
+
         // This pipe depth compensates for scheduling latency of the normal mixer thread.
         // When it wakes up after a maximum latency, it runs a few cycles quickly before
         // finally blocking.  Note the pipe implementation rounds up the request to a power of 2.
@@ -2682,6 +2805,8 @@
         // wrap the source side of the MonoPipe to make it an AudioBufferProvider
         fastTrack->mBufferProvider = new SourceAudioBufferProvider(new MonoPipeReader(monoPipe));
         fastTrack->mVolumeProvider = NULL;
+        fastTrack->mChannelMask = mChannelMask; // mPipeSink channel mask for audio to FastMixer
+        fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer
         fastTrack->mGeneration++;
         state->mFastTracksGen++;
         state->mTrackMask = 1;
@@ -2751,7 +2876,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;
@@ -2808,7 +2933,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) {
@@ -3134,6 +3259,7 @@
                     fastTrack->mBufferProvider = eabp;
                     fastTrack->mVolumeProvider = vp;
                     fastTrack->mChannelMask = track->mChannelMask;
+                    fastTrack->mFormat = track->mFormat;
                     fastTrack->mGeneration++;
                     state->mTrackMask |= 1 << j;
                     didModify = true;
@@ -3255,21 +3381,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
 
@@ -3296,6 +3424,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;
@@ -3522,9 +3651,10 @@
 }
 
 // getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, int sessionId)
+int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask,
+        audio_format_t format, int sessionId)
 {
-    return mAudioMixer->getTrackName(channelMask, sessionId);
+    return mAudioMixer->getTrackName(channelMask, format, sessionId);
 }
 
 // deleteTrackName_l() must be called with ThreadBase::mLock held
@@ -3637,7 +3767,8 @@
             delete mAudioMixer;
             mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
             for (size_t i = 0; i < mTracks.size() ; i++) {
-                int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId);
+                int name = getTrackName_l(mTracks[i]->mChannelMask,
+                        mTracks[i]->mFormat, mTracks[i]->mSessionId);
                 if (name < 0) {
                     break;
                 }
@@ -3669,7 +3800,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);
@@ -3750,13 +3881,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) {
@@ -3924,7 +4059,7 @@
 
 // getTrackName_l() must be called with ThreadBase::mLock held
 int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused,
-        int sessionId __unused)
+        audio_format_t format __unused, int sessionId __unused)
 {
     return 0;
 }
@@ -4152,7 +4287,10 @@
         mMixerStatus = MIXER_DRAIN_ALL;
         threadLoop_drain();
     }
-    mCallbackThread->exit();
+    if (mUseAsyncWrite) {
+        ALOG_ASSERT(mCallbackThread != 0);
+        mCallbackThread->exit();
+    }
     PlaybackThread::threadLoop_exit();
 }
 
@@ -5117,6 +5255,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);
@@ -5349,12 +5488,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);
@@ -5369,9 +5508,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];
@@ -5385,7 +5524,7 @@
             }
         }
     } else {
-        fdprintf(fd, "\n");
+        dprintf(fd, "\n");
     }
 
     if (numactiveseen != numactive) {
@@ -5564,8 +5703,8 @@
                 reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
                 (mInput->stream->common.get_sample_rate(&mInput->stream->common)
                         <= (2 * samplingRate)) &&
-                popcount(mInput->stream->common.get_channels(&mInput->stream->common))
-                        <= FCC_2 &&
+                audio_channel_count_from_in_mask(
+                        mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_2 &&
                 (channelMask == AUDIO_CHANNEL_IN_MONO ||
                         channelMask == AUDIO_CHANNEL_IN_STEREO)) {
                 status = NO_ERROR;
@@ -5619,7 +5758,7 @@
 {
     mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
     mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
-    mChannelCount = popcount(mChannelMask);
+    mChannelCount = audio_channel_count_from_in_mask(mChannelMask);
     mFormat = mInput->stream->common.get_format(&mInput->stream->common);
     if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
         ALOGE("HAL format %#x not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
@@ -5732,4 +5871,61 @@
     return 0;
 }
 
+status_t AudioFlinger::RecordThread::createAudioPatch_l(const struct audio_patch *patch,
+                                                          audio_patch_handle_t *handle)
+{
+    status_t status = NO_ERROR;
+    if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+        // store new device and send to effects
+        mInDevice = patch->sources[0].ext.device.type;
+        for (size_t i = 0; i < mEffectChains.size(); i++) {
+            mEffectChains[i]->setDevice_l(mInDevice);
+        }
+
+        // disable AEC and NS if the device is a BT SCO headset supporting those
+        // pre processings
+        if (mTracks.size() > 0) {
+            bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+                                mAudioFlinger->btNrecIsOff();
+            for (size_t i = 0; i < mTracks.size(); i++) {
+                sp<RecordTrack> track = mTracks[i];
+                setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
+                setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
+            }
+        }
+
+        // store new source and send to effects
+        if (mAudioSource != patch->sinks[0].ext.mix.usecase.source) {
+            mAudioSource = patch->sinks[0].ext.mix.usecase.source;
+            for (size_t i = 0; i < mEffectChains.size(); i++) {
+                mEffectChains[i]->setAudioSource_l(mAudioSource);
+            }
+        }
+
+        audio_hw_device_t *hwDevice = mInput->audioHwDev->hwDevice();
+        status = hwDevice->create_audio_patch(hwDevice,
+                                               patch->num_sources,
+                                               patch->sources,
+                                               patch->num_sinks,
+                                               patch->sinks,
+                                               handle);
+    } else {
+        ALOG_ASSERT(false, "createAudioPatch_l() called on a pre 3.0 HAL");
+    }
+    return status;
+}
+
+status_t AudioFlinger::RecordThread::releaseAudioPatch_l(const audio_patch_handle_t handle)
+{
+    status_t status = NO_ERROR;
+    if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+        audio_hw_device_t *hwDevice = mInput->audioHwDev->hwDevice();
+        status = hwDevice->release_audio_patch(hwDevice, handle);
+    } else {
+        ALOG_ASSERT(false, "releaseAudioPatch_l() called on a pre 3.0 HAL");
+    }
+    return status;
+}
+
+
 }; // namespace android
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index cc2b246..8c9943c 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -48,6 +48,8 @@
         CFG_EVENT_IO,
         CFG_EVENT_PRIO,
         CFG_EVENT_SET_PARAMETER,
+        CFG_EVENT_CREATE_AUDIO_PATCH,
+        CFG_EVENT_RELEASE_AUDIO_PATCH,
     };
 
     class ConfigEventData: public RefBase {
@@ -161,6 +163,52 @@
         virtual ~SetParameterConfigEvent() {}
     };
 
+    class CreateAudioPatchConfigEventData : public ConfigEventData {
+    public:
+        CreateAudioPatchConfigEventData(const struct audio_patch patch,
+                                        audio_patch_handle_t handle) :
+            mPatch(patch), mHandle(handle) {}
+
+        virtual  void dump(char *buffer, size_t size) {
+            snprintf(buffer, size, "Patch handle: %u\n", mHandle);
+        }
+
+        const struct audio_patch mPatch;
+        audio_patch_handle_t mHandle;
+    };
+
+    class CreateAudioPatchConfigEvent : public ConfigEvent {
+    public:
+        CreateAudioPatchConfigEvent(const struct audio_patch patch,
+                                    audio_patch_handle_t handle) :
+            ConfigEvent(CFG_EVENT_CREATE_AUDIO_PATCH) {
+            mData = new CreateAudioPatchConfigEventData(patch, handle);
+            mWaitStatus = true;
+        }
+        virtual ~CreateAudioPatchConfigEvent() {}
+    };
+
+    class ReleaseAudioPatchConfigEventData : public ConfigEventData {
+    public:
+        ReleaseAudioPatchConfigEventData(const audio_patch_handle_t handle) :
+            mHandle(handle) {}
+
+        virtual  void dump(char *buffer, size_t size) {
+            snprintf(buffer, size, "Patch handle: %u\n", mHandle);
+        }
+
+        audio_patch_handle_t mHandle;
+    };
+
+    class ReleaseAudioPatchConfigEvent : public ConfigEvent {
+    public:
+        ReleaseAudioPatchConfigEvent(const audio_patch_handle_t handle) :
+            ConfigEvent(CFG_EVENT_RELEASE_AUDIO_PATCH) {
+            mData = new ReleaseAudioPatchConfigEventData(handle);
+            mWaitStatus = true;
+        }
+        virtual ~ReleaseAudioPatchConfigEvent() {}
+    };
 
     class PMDeathRecipient : public IBinder::DeathRecipient {
     public:
@@ -209,8 +257,15 @@
                 void        sendIoConfigEvent_l(int event, int param = 0);
                 void        sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
                 status_t    sendSetParameterConfigEvent_l(const String8& keyValuePair);
+                status_t    sendCreateAudioPatchConfigEvent(const struct audio_patch *patch,
+                                                            audio_patch_handle_t *handle);
+                status_t    sendReleaseAudioPatchConfigEvent(audio_patch_handle_t handle);
                 void        processConfigEvents_l();
     virtual     void        cacheParameters_l() = 0;
+    virtual     status_t    createAudioPatch_l(const struct audio_patch *patch,
+                                               audio_patch_handle_t *handle) = 0;
+    virtual     status_t    releaseAudioPatch_l(const audio_patch_handle_t handle) = 0;
+
 
                 // see note at declaration of mStandby, mOutDevice and mInDevice
                 bool        standby() const { return mStandby; }
@@ -301,6 +356,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:
@@ -619,7 +676,8 @@
 
     // Allocate a track name for a given channel mask.
     //   Returns name >= 0 if successful, -1 on failure.
-    virtual int             getTrackName_l(audio_channel_mask_t channelMask, int sessionId) = 0;
+    virtual int             getTrackName_l(audio_channel_mask_t channelMask,
+                                           audio_format_t format, int sessionId) = 0;
     virtual void            deleteTrackName_l(int name) = 0;
 
     // Time to sleep between cycles when:
@@ -641,6 +699,10 @@
 
     virtual     uint32_t    correctLatency_l(uint32_t latency) const;
 
+    virtual     status_t    createAudioPatch_l(const struct audio_patch *patch,
+                                   audio_patch_handle_t *handle);
+    virtual     status_t    releaseAudioPatch_l(const audio_patch_handle_t handle);
+
 private:
 
     friend class AudioFlinger;      // for numerous
@@ -772,7 +834,8 @@
 
 protected:
     virtual     mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
-    virtual     int         getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
+    virtual     int         getTrackName_l(audio_channel_mask_t channelMask,
+                                           audio_format_t format, int sessionId);
     virtual     void        deleteTrackName_l(int name);
     virtual     uint32_t    idleSleepTimeUs() const;
     virtual     uint32_t    suspendSleepTimeUs() const;
@@ -825,7 +888,8 @@
                                                    status_t& status);
 
 protected:
-    virtual     int         getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
+    virtual     int         getTrackName_l(audio_channel_mask_t channelMask,
+                                           audio_format_t format, int sessionId);
     virtual     void        deleteTrackName_l(int name);
     virtual     uint32_t    activeSleepTimeUs() const;
     virtual     uint32_t    idleSleepTimeUs() const;
@@ -1030,6 +1094,9 @@
     virtual void        cacheParameters_l() {}
     virtual String8     getParameters(const String8& keys);
     virtual void        audioConfigChanged(int event, int param = 0);
+    virtual status_t    createAudioPatch_l(const struct audio_patch *patch,
+                                           audio_patch_handle_t *handle);
+    virtual status_t    releaseAudioPatch_l(const audio_patch_handle_t handle);
             void        readInputParameters_l();
     virtual uint32_t    getInputFramesLost();
 
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 6dc7f30..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),
@@ -81,7 +83,9 @@
         mSampleRate(sampleRate),
         mFormat(format),
         mChannelMask(channelMask),
-        mChannelCount(popcount(channelMask)),
+        mChannelCount(isOut ?
+                audio_channel_count_from_out_mask(channelMask) :
+                audio_channel_count_from_in_mask(channelMask)),
         mFrameSize(audio_is_linear_pcm(format) ?
                 mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),
         mFrameCount(frameCount),
@@ -113,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;
     }
 
@@ -135,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 ||
@@ -149,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);
@@ -160,6 +175,7 @@
                 mCblk->mFlags = CBLK_FORCEREADY;    // FIXME hack, need to fix the track ready logic
 #endif
             }
+            break;
         }
 
 #ifdef TEE_SINK
@@ -381,7 +397,7 @@
     }
     mServerProxy = mAudioTrackServerProxy;
 
-    mName = thread->getTrackName_l(channelMask, sessionId);
+    mName = thread->getTrackName_l(channelMask, format, sessionId);
     if (mName < 0) {
         ALOGE("no more track names available");
         return;
@@ -459,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) {
@@ -532,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,
@@ -959,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;
 }
@@ -1005,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;
 }
 
@@ -1590,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,
@@ -1838,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)
@@ -1849,7 +1865,7 @@
 
     mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount, mFrameSize);
 
-    uint32_t channelCount = popcount(channelMask);
+    uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
     // FIXME I don't understand either of the channel count checks
     if (thread->mSampleRate != sampleRate && thread->mChannelCount <= FCC_2 &&
             channelCount <= FCC_2) {
@@ -1934,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/AudioPolicyClientImpl.cpp b/services/audiopolicy/AudioPolicyClientImpl.cpp
index 44c47c3..c322d92 100644
--- a/services/audiopolicy/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/AudioPolicyClientImpl.cpp
@@ -182,6 +182,34 @@
     return af->moveEffects(session, src_output, dst_output);
 }
 
+status_t AudioPolicyService::AudioPolicyClient::createAudioPatch(const struct audio_patch *patch,
+                                                                  audio_patch_handle_t *handle,
+                                                                  int delayMs)
+{
+    return mAudioPolicyService->clientCreateAudioPatch(patch, handle, delayMs);
+}
 
+status_t AudioPolicyService::AudioPolicyClient::releaseAudioPatch(audio_patch_handle_t handle,
+                                                                  int delayMs)
+{
+    return mAudioPolicyService->clientReleaseAudioPatch(handle, delayMs);
+}
+
+status_t AudioPolicyService::AudioPolicyClient::setAudioPortConfig(
+                                                        const struct audio_port_config *config,
+                                                        int delayMs)
+{
+    return mAudioPolicyService->clientSetAudioPortConfig(config, delayMs);
+}
+
+void AudioPolicyService::AudioPolicyClient::onAudioPortListUpdate()
+{
+    mAudioPolicyService->onAudioPortListUpdate();
+}
+
+void AudioPolicyService::AudioPolicyClient::onAudioPatchListUpdate()
+{
+    mAudioPolicyService->onAudioPatchListUpdate();
+}
 
 }; // namespace android
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 66260e3..c025a45 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -162,6 +162,24 @@
     virtual status_t    dump(int fd) = 0;
 
     virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo) = 0;
+
+    virtual status_t listAudioPorts(audio_port_role_t role,
+                                    audio_port_type_t type,
+                                    unsigned int *num_ports,
+                                    struct audio_port *ports,
+                                    unsigned int *generation) = 0;
+    virtual status_t getAudioPort(struct audio_port *port) = 0;
+    virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle,
+                                       uid_t uid) = 0;
+    virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+                                          uid_t uid) = 0;
+    virtual status_t listAudioPatches(unsigned int *num_patches,
+                                      struct audio_patch *patches,
+                                      unsigned int *generation) = 0;
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+    virtual void clearAudioPatches(uid_t uid) = 0;
+
 };
 
 
@@ -246,6 +264,21 @@
                                      audio_io_handle_t srcOutput,
                                      audio_io_handle_t dstOutput) = 0;
 
+    /* Create a patch between several source and sink ports */
+    virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle,
+                                       int delayMs) = 0;
+
+    /* Release a patch */
+    virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+                                       int delayMs) = 0;
+
+    /* Set audio port configuration */
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs) = 0;
+
+    virtual void onAudioPortListUpdate() = 0;
+
+    virtual void onAudioPatchListUpdate() = 0;
 };
 
 extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);
diff --git a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
index c57c4fa..2b33703 100644
--- a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
@@ -463,5 +463,72 @@
     return mAudioPolicyManager->isOffloadSupported(info);
 }
 
+status_t AudioPolicyService::listAudioPorts(audio_port_role_t role,
+                                            audio_port_type_t type,
+                                            unsigned int *num_ports,
+                                            struct audio_port *ports,
+                                            unsigned int *generation)
+{
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->listAudioPorts(role, type, num_ports, ports, generation);
+}
+
+status_t AudioPolicyService::getAudioPort(struct audio_port *port)
+{
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->getAudioPort(port);
+}
+
+status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch,
+        audio_patch_handle_t *handle)
+{
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+    return mAudioPolicyManager->createAudioPatch(patch, handle,
+                                                  IPCThreadState::self()->getCallingUid());
+}
+
+status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle)
+{
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->releaseAudioPatch(handle,
+                                                     IPCThreadState::self()->getCallingUid());
+}
+
+status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches,
+        struct audio_patch *patches,
+        unsigned int *generation)
+{
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->listAudioPatches(num_patches, patches, generation);
+}
+
+status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config)
+{
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->setAudioPortConfig(config);
+}
 
 }; // namespace android
diff --git a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp
index bb62ab3..0bf4982 100644
--- a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp
+++ b/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp
@@ -485,5 +485,43 @@
     return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info);
 }
 
+status_t AudioPolicyService::listAudioPorts(audio_port_role_t role __unused,
+                                            audio_port_type_t type __unused,
+                                            unsigned int *num_ports,
+                                            struct audio_port *ports __unused,
+                                            unsigned int *generation __unused)
+{
+    *num_ports = 0;
+    return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::getAudioPort(struct audio_port *port __unused)
+{
+    return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch __unused,
+        audio_patch_handle_t *handle __unused)
+{
+    return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle __unused)
+{
+    return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches,
+        struct audio_patch *patches __unused,
+        unsigned int *generation __unused)
+{
+    *num_patches = 0;
+    return INVALID_OPERATION;
+}
+
+status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config __unused)
+{
+    return INVALID_OPERATION;
+}
 
 }; // namespace android
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index 62a44ee..bf5b9a8 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -38,9 +38,9 @@
 #include <utils/Log.h>
 #include <hardware/audio.h>
 #include <hardware/audio_effect.h>
-#include <hardware_legacy/audio_policy_conf.h>
 #include <media/AudioParameter.h>
 #include "AudioPolicyManager.h"
+#include "audio_policy_conf.h"
 
 namespace android {
 
@@ -70,24 +70,37 @@
     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),
+    STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP),
 };
 
 const StringToEnum sFlagNameToEnumTable[] = {
@@ -124,6 +137,12 @@
     STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
 };
 
+const StringToEnum sGainModeNameToEnumTable[] = {
+    STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT),
+    STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS),
+    STRING_TO_ENUM(AUDIO_GAIN_MODE_RAMP),
+};
+
 
 uint32_t AudioPolicyManager::stringToEnum(const struct StringToEnum *table,
                                               size_t size,
@@ -176,9 +195,8 @@
     if (audio_is_output_device(device)) {
         SortedVector <audio_io_handle_t> outputs;
 
-        sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
-                                                            address,
-                                                            0);
+        sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+        devDesc->mAddress = address;
         ssize_t index = mAvailableOutputDevices.indexOf(devDesc);
 
         // save a copy of the opened output descriptors before any output is opened or closed
@@ -197,12 +215,19 @@
             if (checkOutputsForDevice(device, state, outputs, address) != NO_ERROR) {
                 return INVALID_OPERATION;
             }
+            // outputs should never be empty here
+            ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():"
+                    "checkOutputsForDevice() returned no outputs but status OK");
             ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs",
                   outputs.size());
             // register new device as available
             index = mAvailableOutputDevices.add(devDesc);
             if (index >= 0) {
                 mAvailableOutputDevices[index]->mId = nextUniqueId();
+                HwModule *module = getModuleForDevice(device);
+                ALOG_ASSERT(module != NULL, "setDeviceConnectionState():"
+                        "could not find HW module for device %08x", device);
+                mAvailableOutputDevices[index]->mModule = module;
             } else {
                 return NO_MEMORY;
             }
@@ -255,30 +280,21 @@
             // also force a device 0 for the two outputs it is duplicated to which may override
             // a valid device selection on those outputs.
             setOutputDevice(mOutputs.keyAt(i),
-                            getNewDevice(mOutputs.keyAt(i), true /*fromCache*/),
+                            getNewOutputDevice(mOutputs.keyAt(i), true /*fromCache*/),
                             !mOutputs.valueAt(i)->isDuplicated(),
                             0);
         }
 
-        if (device == AUDIO_DEVICE_OUT_WIRED_HEADSET) {
-            device = AUDIO_DEVICE_IN_WIRED_HEADSET;
-        } else if (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO ||
-                   device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
-                   device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
-            device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
-        } else {
-            return NO_ERROR;
-        }
+        mpClientInterface->onAudioPortListUpdate();
+        return NO_ERROR;
     }  // end if is output device
 
     // handle input devices
     if (audio_is_input_device(device)) {
         SortedVector <audio_io_handle_t> inputs;
 
-        sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
-                                                            address,
-                                                            0);
-
+        sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+        devDesc->mAddress = address;
         ssize_t index = mAvailableInputDevices.indexOf(devDesc);
         switch (state)
         {
@@ -288,6 +304,12 @@
                 ALOGW("setDeviceConnectionState() device already connected: %d", device);
                 return INVALID_OPERATION;
             }
+            HwModule *module = getModuleForDevice(device);
+            if (module == NULL) {
+                ALOGW("setDeviceConnectionState(): could not find HW module for device %08x",
+                      device);
+                return INVALID_OPERATION;
+            }
             if (checkInputsForDevice(device, state, inputs, address) != NO_ERROR) {
                 return INVALID_OPERATION;
             }
@@ -295,6 +317,7 @@
             index = mAvailableInputDevices.add(devDesc);
             if (index >= 0) {
                 mAvailableInputDevices[index]->mId = nextUniqueId();
+                mAvailableInputDevices[index]->mModule = module;
             } else {
                 return NO_MEMORY;
             }
@@ -317,6 +340,7 @@
 
         closeAllInputs();
 
+        mpClientInterface->onAudioPortListUpdate();
         return NO_ERROR;
     } // end if is input device
 
@@ -329,9 +353,8 @@
 {
     audio_policy_dev_state_t state = AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
     String8 address = String8(device_address);
-    sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
-                                                        String8(device_address),
-                                                        0);
+    sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+    devDesc->mAddress = String8(device_address);
     ssize_t index;
     DeviceVector *deviceVector;
 
@@ -407,7 +430,7 @@
     }
 
     // check for device and output changes triggered by new phone state
-    newDevice = getNewDevice(mPrimaryOutput, false /*fromCache*/);
+    newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
     checkA2dpSuspend();
     checkOutputForAllStrategies();
     updateDevicesAndOutputs();
@@ -532,7 +555,7 @@
     updateDevicesAndOutputs();
     for (size_t i = 0; i < mOutputs.size(); i++) {
         audio_io_handle_t output = mOutputs.keyAt(i);
-        audio_devices_t newDevice = getNewDevice(output, true /*fromCache*/);
+        audio_devices_t newDevice = getNewOutputDevice(output, true /*fromCache*/);
         setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE));
         if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {
             applyStreamVolumes(output, newDevice, 0, true);
@@ -541,16 +564,7 @@
 
     audio_io_handle_t activeInput = getActiveInput();
     if (activeInput != 0) {
-        AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
-        audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
-        if ((newDevice != AUDIO_DEVICE_NONE) && (newDevice != inputDesc->mDevice)) {
-            ALOGV("setForceUse() changing device from %x to %x for input %d",
-                    inputDesc->mDevice, newDevice, activeInput);
-            inputDesc->mDevice = newDevice;
-            AudioParameter param = AudioParameter();
-            param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
-            mpClientInterface->setParameters(activeInput, param.toString());
-        }
+        setInputDevice(activeInput, getNewInputDevice(activeInput));
     }
 
 }
@@ -567,7 +581,7 @@
 
 // Find a direct output profile compatible with the parameters passed, even if the input flags do
 // not explicitly request a direct output
-AudioPolicyManager::IOProfile *AudioPolicyManager::getProfileForDirectOutput(
+sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getProfileForDirectOutput(
                                                                audio_devices_t device,
                                                                uint32_t samplingRate,
                                                                audio_format_t format,
@@ -579,7 +593,7 @@
             continue;
         }
         for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) {
-            IOProfile *profile = mHwModules[i]->mOutputProfiles[j];
+            sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j];
             bool found = false;
             if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
                 if (profile->isCompatibleProfile(device, samplingRate, format,
@@ -664,7 +678,7 @@
     // FIXME: We should check the audio session here but we do not have it in this context.
     // This may prevent offloading in rare situations where effects are left active by apps
     // in the background.
-    IOProfile *profile = NULL;
+    sp<IOProfile> profile;
     if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) ||
             !isNonOffloadableEffectEnabled()) {
         profile = getProfileForDirectOutput(device,
@@ -674,7 +688,7 @@
                                            (audio_output_flags_t)flags);
     }
 
-    if (profile != NULL) {
+    if (profile != 0) {
         AudioOutputDescriptor *outputDesc = NULL;
 
         for (size_t i = 0; i < mOutputs.size(); i++) {
@@ -693,7 +707,7 @@
         }
         // close direct output if currently open and configured with different parameters
         if (outputDesc != NULL) {
-            closeOutput(outputDesc->mId);
+            closeOutput(outputDesc->mIoHandle);
         }
         outputDesc = new AudioOutputDescriptor(profile);
         outputDesc->mDevice = device;
@@ -737,6 +751,7 @@
         }
         mPreviousOutputs = mOutputs;
         ALOGV("getOutput() returns new direct output %d", output);
+        mpClientInterface->onAudioPortListUpdate();
         return output;
     }
 
@@ -825,7 +840,7 @@
     outputDesc->changeRefCount(stream, 1);
 
     if (outputDesc->mRefCount[stream] == 1) {
-        audio_devices_t newDevice = getNewDevice(output, false /*fromCache*/);
+        audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/);
         routing_strategy strategy = getStrategy(stream);
         bool shouldWait = (strategy == STRATEGY_SONIFICATION) ||
                             (strategy == STRATEGY_SONIFICATION_RESPECTFUL);
@@ -898,7 +913,7 @@
         // store time at which the stream was stopped - see isStreamActive()
         if (outputDesc->mRefCount[stream] == 0) {
             outputDesc->mStopTime[stream] = systemTime();
-            audio_devices_t newDevice = getNewDevice(output, false /*fromCache*/);
+            audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/);
             // delay the device switch by twice the latency because stopOutput() is executed when
             // the track stop() command is received and at that time the audio track buffer can
             // still contain data that needs to be drained. The latency only covers the audio HAL
@@ -916,7 +931,7 @@
                         outputDesc->sharesHwModuleWith(desc) &&
                         (newDevice != desc->device())) {
                     setOutputDevice(curOutput,
-                                    getNewDevice(curOutput, false /*fromCache*/),
+                                    getNewOutputDevice(curOutput, false /*fromCache*/),
                                     true,
                                     outputDesc->mLatency*2);
                 }
@@ -969,6 +984,7 @@
             if (dstOutput != mPrimaryOutput) {
                 mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
             }
+            mpClientInterface->onAudioPortListUpdate();
         }
     }
 }
@@ -1006,11 +1022,11 @@
         break;
     }
 
-    IOProfile *profile = getInputProfile(device,
+    sp<IOProfile> profile = getInputProfile(device,
                                          samplingRate,
                                          format,
                                          channelMask);
-    if (profile == NULL) {
+    if (profile == 0) {
         ALOGW("getInput() could not find profile for device %04x, samplingRate %d, format %d, "
                 "channelMask %04x",
                 device, samplingRate, format, channelMask);
@@ -1050,6 +1066,7 @@
         return 0;
     }
     addInput(input, inputDesc);
+    mpClientInterface->onAudioPortListUpdate();
     return input;
 }
 
@@ -1083,10 +1100,7 @@
         }
     }
 
-    audio_devices_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
-    if ((newDevice != AUDIO_DEVICE_NONE) && (newDevice != inputDesc->mDevice)) {
-        inputDesc->mDevice = newDevice;
-    }
+    setInputDevice(input, getNewInputDevice(input), true /* force */);
 
     // automatically enable the remote submix output when input is started
     if (audio_is_remote_submix_device(inputDesc->mDevice)) {
@@ -1094,17 +1108,8 @@
                 AUDIO_POLICY_DEVICE_STATE_AVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS);
     }
 
-    AudioParameter param = AudioParameter();
-    param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice);
-
-    int aliasSource = (inputDesc->mInputSource == AUDIO_SOURCE_HOTWORD) ?
-                                        AUDIO_SOURCE_VOICE_RECOGNITION : inputDesc->mInputSource;
-
-    param.addInt(String8(AudioParameter::keyInputSource), aliasSource);
     ALOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource);
 
-    mpClientInterface->setParameters(input, param.toString());
-
     inputDesc->mRefCount = 1;
     return NO_ERROR;
 }
@@ -1129,9 +1134,7 @@
                     AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS);
         }
 
-        AudioParameter param = AudioParameter();
-        param.addInt(String8(AudioParameter::keyRouting), 0);
-        mpClientInterface->setParameters(input, param.toString());
+        resetInputDevice(input);
         inputDesc->mRefCount = 0;
         return NO_ERROR;
     }
@@ -1148,6 +1151,8 @@
     mpClientInterface->closeInput(input);
     delete mInputs.valueAt(index);
     mInputs.removeItem(input);
+    nextAudioPortGeneration();
+    mpClientInterface->onAudioPortListUpdate();
     ALOGV("releaseInput() exit");
 }
 
@@ -1156,6 +1161,7 @@
         mpClientInterface->closeInput(mInputs.keyAt(input_index));
     }
     mInputs.clear();
+    nextAudioPortGeneration();
 }
 
 void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream,
@@ -1476,15 +1482,13 @@
     snprintf(buffer, SIZE, " Available output devices:\n");
     result.append(buffer);
     write(fd, result.string(), result.size());
-    DeviceDescriptor::dumpHeader(fd, 2);
     for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
-        mAvailableOutputDevices[i]->dump(fd, 2);
+        mAvailableOutputDevices[i]->dump(fd, 2, i);
     }
     snprintf(buffer, SIZE, "\n Available input devices:\n");
     write(fd, buffer, strlen(buffer));
-    DeviceDescriptor::dumpHeader(fd, 2);
     for (size_t i = 0; i < mAvailableInputDevices.size(); i++) {
-        mAvailableInputDevices[i]->dump(fd, 2);
+        mAvailableInputDevices[i]->dump(fd, 2, i);
     }
 
     snprintf(buffer, SIZE, "\nHW Modules dump:\n");
@@ -1596,13 +1600,556 @@
 
     // See if there is a profile to support this.
     // AUDIO_DEVICE_NONE
-    IOProfile *profile = getProfileForDirectOutput(AUDIO_DEVICE_NONE /*ignore device */,
+    sp<IOProfile> profile = getProfileForDirectOutput(AUDIO_DEVICE_NONE /*ignore device */,
                                             offloadInfo.sample_rate,
                                             offloadInfo.format,
                                             offloadInfo.channel_mask,
                                             AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
-    ALOGV("isOffloadSupported() profile %sfound", profile != NULL ? "" : "NOT ");
-    return (profile != NULL);
+    ALOGV("isOffloadSupported() profile %sfound", profile != 0 ? "" : "NOT ");
+    return (profile != 0);
+}
+
+status_t AudioPolicyManager::listAudioPorts(audio_port_role_t role,
+                                            audio_port_type_t type,
+                                            unsigned int *num_ports,
+                                            struct audio_port *ports,
+                                            unsigned int *generation)
+{
+    if (num_ports == NULL || (*num_ports != 0 && ports == NULL) ||
+            generation == NULL) {
+        return BAD_VALUE;
+    }
+    ALOGV("listAudioPorts() role %d type %d num_ports %d ports %p", role, type, *num_ports, ports);
+    if (ports == NULL) {
+        *num_ports = 0;
+    }
+
+    size_t portsWritten = 0;
+    size_t portsMax = *num_ports;
+    *num_ports = 0;
+    if (type == AUDIO_PORT_TYPE_NONE || type == AUDIO_PORT_TYPE_DEVICE) {
+        if (role == AUDIO_PORT_ROLE_SINK || role == AUDIO_PORT_ROLE_NONE) {
+            for (size_t i = 0;
+                    i  < mAvailableOutputDevices.size() && portsWritten < portsMax; i++) {
+                mAvailableOutputDevices[i]->toAudioPort(&ports[portsWritten++]);
+            }
+            *num_ports += mAvailableOutputDevices.size();
+        }
+        if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
+            for (size_t i = 0;
+                    i  < mAvailableInputDevices.size() && portsWritten < portsMax; i++) {
+                mAvailableInputDevices[i]->toAudioPort(&ports[portsWritten++]);
+            }
+            *num_ports += mAvailableInputDevices.size();
+        }
+    }
+    if (type == AUDIO_PORT_TYPE_NONE || type == AUDIO_PORT_TYPE_MIX) {
+        if (role == AUDIO_PORT_ROLE_SINK || role == AUDIO_PORT_ROLE_NONE) {
+            for (size_t i = 0; i < mInputs.size() && portsWritten < portsMax; i++) {
+                mInputs[i]->toAudioPort(&ports[portsWritten++]);
+            }
+            *num_ports += mInputs.size();
+        }
+        if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
+            for (size_t i = 0; i < mOutputs.size() && portsWritten < portsMax; i++) {
+                mOutputs[i]->toAudioPort(&ports[portsWritten++]);
+            }
+            *num_ports += mOutputs.size();
+        }
+    }
+    *generation = curAudioPortGeneration();
+    ALOGV("listAudioPorts() got %d ports needed %d", portsWritten, *num_ports);
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::getAudioPort(struct audio_port *port __unused)
+{
+    return NO_ERROR;
+}
+
+AudioPolicyManager::AudioOutputDescriptor *AudioPolicyManager::getOutputFromId(
+                                                                    audio_port_handle_t id) const
+{
+    AudioOutputDescriptor *outputDesc = NULL;
+    for (size_t i = 0; i < mOutputs.size(); i++) {
+        outputDesc = mOutputs.valueAt(i);
+        if (outputDesc->mId == id) {
+            break;
+        }
+    }
+    return outputDesc;
+}
+
+AudioPolicyManager::AudioInputDescriptor *AudioPolicyManager::getInputFromId(
+                                                                    audio_port_handle_t id) const
+{
+    AudioInputDescriptor *inputDesc = NULL;
+    for (size_t i = 0; i < mInputs.size(); i++) {
+        inputDesc = mInputs.valueAt(i);
+        if (inputDesc->mId == id) {
+            break;
+        }
+    }
+    return inputDesc;
+}
+
+AudioPolicyManager::HwModule *AudioPolicyManager::getModuleForDevice(audio_devices_t device) const
+{
+    for (size_t i = 0; i < mHwModules.size(); i++) {
+        if (mHwModules[i]->mHandle == 0) {
+            continue;
+        }
+        if (audio_is_output_device(device)) {
+            for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
+            {
+                if (mHwModules[i]->mOutputProfiles[j]->mSupportedDevices.types() & device) {
+                    return mHwModules[i];
+                }
+            }
+        } else {
+            for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) {
+                if (mHwModules[i]->mInputProfiles[j]->mSupportedDevices.types() &
+                        device & ~AUDIO_DEVICE_BIT_IN) {
+                    return mHwModules[i];
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+AudioPolicyManager::HwModule *AudioPolicyManager::getModuleFromName(const char *name) const
+{
+    for (size_t i = 0; i < mHwModules.size(); i++)
+    {
+        if (strcmp(mHwModules[i]->mName, name) == 0) {
+            return mHwModules[i];
+        }
+    }
+    return NULL;
+}
+
+
+status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
+                                               audio_patch_handle_t *handle,
+                                               uid_t uid)
+{
+    ALOGV("createAudioPatch()");
+
+    if (handle == NULL || patch == NULL) {
+        return BAD_VALUE;
+    }
+    ALOGV("createAudioPatch() num sources %d num sinks %d", patch->num_sources, patch->num_sinks);
+
+    if (patch->num_sources > 1 || patch->num_sinks > 1) {
+        return INVALID_OPERATION;
+    }
+    if (patch->sources[0].role != AUDIO_PORT_ROLE_SOURCE ||
+            patch->sinks[0].role != AUDIO_PORT_ROLE_SINK) {
+        return INVALID_OPERATION;
+    }
+
+    sp<AudioPatch> patchDesc;
+    ssize_t index = mAudioPatches.indexOfKey(*handle);
+
+    ALOGV("createAudioPatch sink id %d role %d type %d", patch->sinks[0].id, patch->sinks[0].role,
+                                                         patch->sinks[0].type);
+    ALOGV("createAudioPatch source id %d role %d type %d", patch->sources[0].id,
+                                                           patch->sources[0].role,
+                                                           patch->sources[0].type);
+
+    if (index >= 0) {
+        patchDesc = mAudioPatches.valueAt(index);
+        ALOGV("createAudioPatch() mUidCached %d patchDesc->mUid %d uid %d",
+                                                                  mUidCached, patchDesc->mUid, uid);
+        if (patchDesc->mUid != mUidCached && uid != patchDesc->mUid) {
+            return INVALID_OPERATION;
+        }
+    } else {
+        *handle = 0;
+    }
+
+    if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
+        // TODO add support for mix to mix connection
+        if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
+            ALOGV("createAudioPatch() source mix sink not device");
+            return BAD_VALUE;
+        }
+        // output mix to output device connection
+        AudioOutputDescriptor *outputDesc = getOutputFromId(patch->sources[0].id);
+        if (outputDesc == NULL) {
+            ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
+            return BAD_VALUE;
+        }
+        if (patchDesc != 0) {
+            if (patchDesc->mPatch.sources[0].id != patch->sources[0].id) {
+                ALOGV("createAudioPatch() source id differs for patch current id %d new id %d",
+                                          patchDesc->mPatch.sources[0].id, patch->sources[0].id);
+                return BAD_VALUE;
+            }
+        }
+        sp<DeviceDescriptor> devDesc =
+                mAvailableOutputDevices.getDeviceFromId(patch->sinks[0].id);
+        if (devDesc == 0) {
+            ALOGV("createAudioPatch() out device not found for id %d", patch->sinks[0].id);
+            return BAD_VALUE;
+        }
+
+        if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mType,
+                                                       patch->sources[0].sample_rate,
+                                                     patch->sources[0].format,
+                                                     patch->sources[0].channel_mask,
+                                                     AUDIO_OUTPUT_FLAG_NONE)) {
+            return INVALID_OPERATION;
+        }
+        // TODO: reconfigure output format and channels here
+        ALOGV("createAudioPatch() setting device %08x on output %d",
+                                              devDesc->mType, outputDesc->mIoHandle);
+        setOutputDevice(outputDesc->mIoHandle,
+                        devDesc->mType,
+                       true,
+                       0,
+                       handle);
+        index = mAudioPatches.indexOfKey(*handle);
+        if (index >= 0) {
+            if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
+                ALOGW("createAudioPatch() setOutputDevice() did not reuse the patch provided");
+            }
+            patchDesc = mAudioPatches.valueAt(index);
+            patchDesc->mUid = uid;
+            ALOGV("createAudioPatch() success");
+        } else {
+            ALOGW("createAudioPatch() setOutputDevice() failed to create a patch");
+            return INVALID_OPERATION;
+        }
+    } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+        if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+            // input device to input mix connection
+            AudioInputDescriptor *inputDesc = getInputFromId(patch->sinks[0].id);
+            if (inputDesc == NULL) {
+                return BAD_VALUE;
+            }
+            if (patchDesc != 0) {
+                if (patchDesc->mPatch.sinks[0].id != patch->sinks[0].id) {
+                    return BAD_VALUE;
+                }
+            }
+            sp<DeviceDescriptor> devDesc =
+                    mAvailableInputDevices.getDeviceFromId(patch->sources[0].id);
+            if (devDesc == 0) {
+                return BAD_VALUE;
+            }
+
+            if (!inputDesc->mProfile->isCompatibleProfile(devDesc->mType,
+                                                           patch->sinks[0].sample_rate,
+                                                         patch->sinks[0].format,
+                                                         patch->sinks[0].channel_mask,
+                                                         AUDIO_OUTPUT_FLAG_NONE)) {
+                return INVALID_OPERATION;
+            }
+            // TODO: reconfigure output format and channels here
+            ALOGV("createAudioPatch() setting device %08x on output %d",
+                                                  devDesc->mType, inputDesc->mIoHandle);
+            setInputDevice(inputDesc->mIoHandle,
+                           devDesc->mType,
+                           true,
+                           handle);
+            index = mAudioPatches.indexOfKey(*handle);
+            if (index >= 0) {
+                if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
+                    ALOGW("createAudioPatch() setInputDevice() did not reuse the patch provided");
+                }
+                patchDesc = mAudioPatches.valueAt(index);
+                patchDesc->mUid = uid;
+                ALOGV("createAudioPatch() success");
+            } else {
+                ALOGW("createAudioPatch() setInputDevice() failed to create a patch");
+                return INVALID_OPERATION;
+            }
+        } else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
+            // device to device connection
+            if (patchDesc != 0) {
+                if (patchDesc->mPatch.sources[0].id != patch->sources[0].id &&
+                    patchDesc->mPatch.sinks[0].id != patch->sinks[0].id) {
+                    return BAD_VALUE;
+                }
+            }
+
+            sp<DeviceDescriptor> srcDeviceDesc =
+                    mAvailableInputDevices.getDeviceFromId(patch->sources[0].id);
+            sp<DeviceDescriptor> sinkDeviceDesc =
+                    mAvailableOutputDevices.getDeviceFromId(patch->sinks[0].id);
+            if (srcDeviceDesc == 0 || sinkDeviceDesc == 0) {
+                return BAD_VALUE;
+            }
+            //update source and sink with our own data as the data passed in the patch may
+            // be incomplete.
+            struct audio_patch newPatch = *patch;
+            srcDeviceDesc->toAudioPortConfig(&newPatch.sources[0], &patch->sources[0]);
+            sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[0], &patch->sinks[0]);
+
+            // TODO: add support for devices on different HW modules
+            if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
+                return INVALID_OPERATION;
+            }
+            // TODO: check from routing capabilities in config file and other conflicting patches
+
+            audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+            if (index >= 0) {
+                afPatchHandle = patchDesc->mAfPatchHandle;
+            }
+
+            status_t status = mpClientInterface->createAudioPatch(&newPatch,
+                                                                  &afPatchHandle,
+                                                                  0);
+            ALOGV("createAudioPatch() patch panel returned %d patchHandle %d",
+                                                                  status, afPatchHandle);
+            if (status == NO_ERROR) {
+                if (index < 0) {
+                    patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+                                               &newPatch, uid);
+                    addAudioPatch(patchDesc->mHandle, patchDesc);
+                } else {
+                    patchDesc->mPatch = newPatch;
+                }
+                patchDesc->mAfPatchHandle = afPatchHandle;
+                *handle = patchDesc->mHandle;
+                nextAudioPortGeneration();
+                mpClientInterface->onAudioPatchListUpdate();
+            } else {
+                ALOGW("createAudioPatch() patch panel could not connect device patch, error %d",
+                status);
+                return INVALID_OPERATION;
+            }
+        } else {
+            return BAD_VALUE;
+        }
+    } else {
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::releaseAudioPatch(audio_patch_handle_t handle,
+                                                  uid_t uid)
+{
+    ALOGV("releaseAudioPatch() patch %d", handle);
+
+    ssize_t index = mAudioPatches.indexOfKey(handle);
+
+    if (index < 0) {
+        return BAD_VALUE;
+    }
+    sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+    ALOGV("releaseAudioPatch() mUidCached %d patchDesc->mUid %d uid %d",
+          mUidCached, patchDesc->mUid, uid);
+    if (patchDesc->mUid != mUidCached && uid != patchDesc->mUid) {
+        return INVALID_OPERATION;
+    }
+
+    struct audio_patch *patch = &patchDesc->mPatch;
+    patchDesc->mUid = mUidCached;
+    if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
+        AudioOutputDescriptor *outputDesc = getOutputFromId(patch->sources[0].id);
+        if (outputDesc == NULL) {
+            ALOGV("releaseAudioPatch() output not found for id %d", patch->sources[0].id);
+            return BAD_VALUE;
+        }
+
+        setOutputDevice(outputDesc->mIoHandle,
+                        getNewOutputDevice(outputDesc->mIoHandle, true /*fromCache*/),
+                       true,
+                       0,
+                       NULL);
+    } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+        if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+            AudioInputDescriptor *inputDesc = getInputFromId(patch->sinks[0].id);
+            if (inputDesc == NULL) {
+                ALOGV("releaseAudioPatch() input not found for id %d", patch->sinks[0].id);
+                return BAD_VALUE;
+            }
+            setInputDevice(inputDesc->mIoHandle,
+                           getNewInputDevice(inputDesc->mIoHandle),
+                           true,
+                           NULL);
+        } else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
+            audio_patch_handle_t afPatchHandle = patchDesc->mAfPatchHandle;
+            status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+            ALOGV("releaseAudioPatch() patch panel returned %d patchHandle %d",
+                                                              status, patchDesc->mAfPatchHandle);
+            removeAudioPatch(patchDesc->mHandle);
+            nextAudioPortGeneration();
+            mpClientInterface->onAudioPatchListUpdate();
+        } else {
+            return BAD_VALUE;
+        }
+    } else {
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::listAudioPatches(unsigned int *num_patches,
+                                              struct audio_patch *patches,
+                                              unsigned int *generation)
+{
+    if (num_patches == NULL || (*num_patches != 0 && patches == NULL) ||
+            generation == NULL) {
+        return BAD_VALUE;
+    }
+    ALOGV("listAudioPatches() num_patches %d patches %p available patches %d",
+          *num_patches, patches, mAudioPatches.size());
+    if (patches == NULL) {
+        *num_patches = 0;
+    }
+
+    size_t patchesWritten = 0;
+    size_t patchesMax = *num_patches;
+    for (size_t i = 0;
+            i  < mAudioPatches.size() && patchesWritten < patchesMax; i++) {
+        patches[patchesWritten] = mAudioPatches[i]->mPatch;
+        patches[patchesWritten++].id = mAudioPatches[i]->mHandle;
+        ALOGV("listAudioPatches() patch %d num_sources %d num_sinks %d",
+              i, mAudioPatches[i]->mPatch.num_sources, mAudioPatches[i]->mPatch.num_sinks);
+    }
+    *num_patches = mAudioPatches.size();
+
+    *generation = curAudioPortGeneration();
+    ALOGV("listAudioPatches() got %d patches needed %d", patchesWritten, *num_patches);
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config *config)
+{
+    ALOGV("setAudioPortConfig()");
+
+    if (config == NULL) {
+        return BAD_VALUE;
+    }
+    ALOGV("setAudioPortConfig() on port handle %d", config->id);
+    // Only support gain configuration for now
+    if (config->config_mask != AUDIO_PORT_CONFIG_GAIN || config->gain.index < 0) {
+        return BAD_VALUE;
+    }
+
+    sp<AudioPort> portDesc;
+    struct audio_port_config portConfig;
+    if (config->type == AUDIO_PORT_TYPE_MIX) {
+        if (config->role == AUDIO_PORT_ROLE_SOURCE) {
+            AudioOutputDescriptor *outputDesc = getOutputFromId(config->id);
+            if (outputDesc == NULL) {
+                return BAD_VALUE;
+            }
+            portDesc = outputDesc->mProfile;
+            outputDesc->toAudioPortConfig(&portConfig);
+        } else if (config->role == AUDIO_PORT_ROLE_SINK) {
+            AudioInputDescriptor *inputDesc = getInputFromId(config->id);
+            if (inputDesc == NULL) {
+                return BAD_VALUE;
+            }
+            portDesc = inputDesc->mProfile;
+            inputDesc->toAudioPortConfig(&portConfig);
+        } else {
+            return BAD_VALUE;
+        }
+    } else if (config->type == AUDIO_PORT_TYPE_DEVICE) {
+        sp<DeviceDescriptor> deviceDesc;
+        if (config->role == AUDIO_PORT_ROLE_SOURCE) {
+            deviceDesc = mAvailableInputDevices.getDeviceFromId(config->id);
+        } else if (config->role == AUDIO_PORT_ROLE_SINK) {
+            deviceDesc = mAvailableOutputDevices.getDeviceFromId(config->id);
+        } else {
+            return BAD_VALUE;
+        }
+        if (deviceDesc == NULL) {
+            return BAD_VALUE;
+        }
+        portDesc = deviceDesc;
+        deviceDesc->toAudioPortConfig(&portConfig);
+    } else {
+        return BAD_VALUE;
+    }
+
+    if ((size_t)config->gain.index >= portDesc->mGains.size()) {
+        return INVALID_OPERATION;
+    }
+    const struct audio_gain *gain = &portDesc->mGains[config->gain.index]->mGain;
+    if ((config->gain.mode & ~gain->mode) != 0) {
+        return BAD_VALUE;
+    }
+    if ((config->gain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) {
+        if ((config->gain.values[0] < gain->min_value) ||
+                    (config->gain.values[0] > gain->max_value)) {
+            return BAD_VALUE;
+        }
+    } else {
+        if ((config->gain.channel_mask & ~gain->channel_mask) != 0) {
+            return BAD_VALUE;
+        }
+        size_t numValues = popcount(config->gain.channel_mask);
+        for (size_t i = 0; i < numValues; i++) {
+            if ((config->gain.values[i] < gain->min_value) ||
+                    (config->gain.values[i] > gain->max_value)) {
+                return BAD_VALUE;
+            }
+        }
+    }
+    if ((config->gain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) {
+        if ((config->gain.ramp_duration_ms < gain->min_ramp_ms) ||
+                    (config->gain.ramp_duration_ms > gain->max_ramp_ms)) {
+            return BAD_VALUE;
+        }
+    }
+
+    portConfig.gain = config->gain;
+
+    status_t status = mpClientInterface->setAudioPortConfig(&portConfig, 0);
+
+    return status;
+}
+
+void AudioPolicyManager::clearAudioPatches(uid_t uid)
+{
+    for (ssize_t i = 0; i < (ssize_t)mAudioPatches.size(); i++)  {
+        sp<AudioPatch> patchDesc = mAudioPatches.valueAt(i);
+        if (patchDesc->mUid == uid) {
+            // releaseAudioPatch() removes the patch from mAudioPatches
+            if (releaseAudioPatch(mAudioPatches.keyAt(i), uid) == NO_ERROR) {
+                i--;
+            }
+        }
+    }
+}
+
+status_t AudioPolicyManager::addAudioPatch(audio_patch_handle_t handle,
+                                           const sp<AudioPatch>& patch)
+{
+    ssize_t index = mAudioPatches.indexOfKey(handle);
+
+    if (index >= 0) {
+        ALOGW("addAudioPatch() patch %d already in", handle);
+        return ALREADY_EXISTS;
+    }
+    mAudioPatches.add(handle, patch);
+    ALOGV("addAudioPatch() handle %d af handle %d num_sources %d num_sinks %d source handle %d"
+            "sink handle %d",
+          handle, patch->mAfPatchHandle, patch->mPatch.num_sources, patch->mPatch.num_sinks,
+          patch->mPatch.sources[0].id, patch->mPatch.sinks[0].id);
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::removeAudioPatch(audio_patch_handle_t handle)
+{
+    ssize_t index = mAudioPatches.indexOfKey(handle);
+
+    if (index < 0) {
+        ALOGW("removeAudioPatch() patch %d not in", handle);
+        return ALREADY_EXISTS;
+    }
+    ALOGV("removeAudioPatch() handle %d af handle %d", handle,
+                      mAudioPatches.valueAt(index)->mAfPatchHandle);
+    mAudioPatches.removeItemsAt(index);
+    return NO_ERROR;
 }
 
 // ----------------------------------------------------------------------------
@@ -1614,6 +2161,11 @@
     return android_atomic_inc(&mNextUniqueId);
 }
 
+uint32_t AudioPolicyManager::nextAudioPortGeneration()
+{
+    return android_atomic_inc(&mAudioPortGeneration);
+}
+
 AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
     :
 #ifdef AUDIO_POLICY_TEST
@@ -1624,15 +2176,17 @@
     mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
     mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
     mA2dpSuspended(false),
-    mSpeakerDrcEnabled(false), mNextUniqueId(0)
+    mSpeakerDrcEnabled(false), mNextUniqueId(1),
+    mAudioPortGeneration(1)
 {
+    mUidCached = getuid();
     mpClientInterface = clientInterface;
 
     for (int i = 0; i < AUDIO_POLICY_FORCE_USE_CNT; i++) {
         mForceUse[i] = AUDIO_POLICY_FORCE_NONE;
     }
 
-    mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER);
+    mDefaultOutputDevice = new DeviceDescriptor(String8(""), AUDIO_DEVICE_OUT_SPEAKER);
     if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE) != NO_ERROR) {
         if (loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE) != NO_ERROR) {
             ALOGE("could not load audio policy configuration file, setting defaults");
@@ -1659,7 +2213,7 @@
         // This also validates mAvailableOutputDevices list
         for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
         {
-            const IOProfile *outProfile = mHwModules[i]->mOutputProfiles[j];
+            const sp<IOProfile> outProfile = mHwModules[i]->mOutputProfiles[j];
 
             if (outProfile->mSupportedDevices.isEmpty()) {
                 ALOGW("Output profile contains no device on module %s", mHwModules[i]->mName);
@@ -1671,7 +2225,7 @@
                     ((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) {
                 AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(outProfile);
 
-                outputDesc->mDevice = (audio_devices_t)(mDefaultOutputDevice->mType & profileTypes);
+                outputDesc->mDevice = (audio_devices_t)(mDefaultOutputDevice->mDeviceType & profileTypes);
                 audio_io_handle_t output = mpClientInterface->openOutput(
                                                 outProfile->mModule->mHandle,
                                                 &outputDesc->mDevice,
@@ -1687,12 +2241,13 @@
                     delete outputDesc;
                 } else {
                     for (size_t k = 0; k  < outProfile->mSupportedDevices.size(); k++) {
-                        audio_devices_t type = outProfile->mSupportedDevices[k]->mType;
+                        audio_devices_t type = outProfile->mSupportedDevices[k]->mDeviceType;
                         ssize_t index =
                                 mAvailableOutputDevices.indexOf(outProfile->mSupportedDevices[k]);
                         // give a valid ID to an attached device once confirmed it is reachable
                         if ((index >= 0) && (mAvailableOutputDevices[index]->mId == 0)) {
                             mAvailableOutputDevices[index]->mId = nextUniqueId();
+                            mAvailableOutputDevices[index]->mModule = mHwModules[i];
                         }
                     }
                     if (mPrimaryOutput == 0 &&
@@ -1700,6 +2255,7 @@
                         mPrimaryOutput = output;
                     }
                     addOutput(output, outputDesc);
+                    ALOGI("CSTOR setOutputDevice %08x", outputDesc->mDevice);
                     setOutputDevice(output,
                                     outputDesc->mDevice,
                                     true);
@@ -1710,7 +2266,7 @@
         // mAvailableInputDevices list
         for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++)
         {
-            const IOProfile *inProfile = mHwModules[i]->mInputProfiles[j];
+            const sp<IOProfile> inProfile = mHwModules[i]->mInputProfiles[j];
 
             if (inProfile->mSupportedDevices.isEmpty()) {
                 ALOGW("Input profile contains no device on module %s", mHwModules[i]->mName);
@@ -1722,7 +2278,7 @@
                 AudioInputDescriptor *inputDesc = new AudioInputDescriptor(inProfile);
 
                 inputDesc->mInputSource = AUDIO_SOURCE_MIC;
-                inputDesc->mDevice = inProfile->mSupportedDevices[0]->mType;
+                inputDesc->mDevice = inProfile->mSupportedDevices[0]->mDeviceType;
                 audio_io_handle_t input = mpClientInterface->openInput(
                                                     inProfile->mModule->mHandle,
                                                     &inputDesc->mDevice,
@@ -1732,12 +2288,13 @@
 
                 if (input != 0) {
                     for (size_t k = 0; k  < inProfile->mSupportedDevices.size(); k++) {
-                        audio_devices_t type = inProfile->mSupportedDevices[k]->mType;
+                        audio_devices_t type = inProfile->mSupportedDevices[k]->mDeviceType;
                         ssize_t index =
                                 mAvailableInputDevices.indexOf(inProfile->mSupportedDevices[k]);
                         // give a valid ID to an attached device once confirmed it is reachable
                         if ((index >= 0) && (mAvailableInputDevices[index]->mId == 0)) {
                             mAvailableInputDevices[index]->mId = nextUniqueId();
+                            mAvailableInputDevices[index]->mModule = mHwModules[i];
                         }
                     }
                     mpClientInterface->closeInput(input);
@@ -1753,7 +2310,7 @@
     // make sure all attached devices have been allocated a unique ID
     for (size_t i = 0; i  < mAvailableOutputDevices.size();) {
         if (mAvailableOutputDevices[i]->mId == 0) {
-            ALOGW("Input device %08x unreachable", mAvailableOutputDevices[i]->mType);
+            ALOGW("Input device %08x unreachable", mAvailableOutputDevices[i]->mDeviceType);
             mAvailableOutputDevices.remove(mAvailableOutputDevices[i]);
             continue;
         }
@@ -1761,7 +2318,7 @@
     }
     for (size_t i = 0; i  < mAvailableInputDevices.size();) {
         if (mAvailableInputDevices[i]->mId == 0) {
-            ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->mType);
+            ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->mDeviceType);
             mAvailableInputDevices.remove(mAvailableInputDevices[i]);
             continue;
         }
@@ -1769,7 +2326,7 @@
     }
     // make sure default device is reachable
     if (mAvailableOutputDevices.indexOf(mDefaultOutputDevice) < 0) {
-        ALOGE("Default device %08x is unreachable", mDefaultOutputDevice->mType);
+        ALOGE("Default device %08x is unreachable", mDefaultOutputDevice->mDeviceType);
     }
 
     ALOGE_IF((mPrimaryOutput == 0), "Failed to open primary output");
@@ -1978,16 +2535,20 @@
 
 // ---
 
-void AudioPolicyManager::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc)
+void AudioPolicyManager::addOutput(audio_io_handle_t output, AudioOutputDescriptor *outputDesc)
 {
-    outputDesc->mId = id;
-    mOutputs.add(id, outputDesc);
+    outputDesc->mIoHandle = output;
+    outputDesc->mId = nextUniqueId();
+    mOutputs.add(output, outputDesc);
+    nextAudioPortGeneration();
 }
 
-void AudioPolicyManager::addInput(audio_io_handle_t id, AudioInputDescriptor *inputDesc)
+void AudioPolicyManager::addInput(audio_io_handle_t input, AudioInputDescriptor *inputDesc)
 {
-    inputDesc->mId = id;
-    mInputs.add(id, inputDesc);
+    inputDesc->mIoHandle = input;
+    inputDesc->mId = nextUniqueId();
+    mInputs.add(input, inputDesc);
+    nextAudioPortGeneration();
 }
 
 String8 AudioPolicyManager::addressToParameter(audio_devices_t device, const String8 address)
@@ -2015,7 +2576,7 @@
             }
         }
         // then look for output profiles that can be routed to this device
-        SortedVector<IOProfile *> profiles;
+        SortedVector< sp<IOProfile> > profiles;
         for (size_t i = 0; i < mHwModules.size(); i++)
         {
             if (mHwModules[i]->mHandle == 0) {
@@ -2038,7 +2599,7 @@
         // open outputs for matching profiles if needed. Direct outputs are also opened to
         // query for dynamic parameters and will be closed later by setDeviceConnectionState()
         for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) {
-            IOProfile *profile = profiles[profile_index];
+            sp<IOProfile> profile = profiles[profile_index];
 
             // nothing to do if one output is already opened for this profile
             size_t j;
@@ -2084,7 +2645,7 @@
                               reply.string());
                     value = strpbrk((char *)reply.string(), "=");
                     if (value != NULL) {
-                        loadSamplingRates(value + 1, profile);
+                        profile->loadSamplingRates(value + 1);
                     }
                 }
                 if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) {
@@ -2094,7 +2655,7 @@
                               reply.string());
                     value = strpbrk((char *)reply.string(), "=");
                     if (value != NULL) {
-                        loadFormats(value + 1, profile);
+                        profile->loadFormats(value + 1);
                     }
                 }
                 if (profile->mChannelMasks[0] == 0) {
@@ -2104,7 +2665,7 @@
                               reply.string());
                     value = strpbrk((char *)reply.string(), "=");
                     if (value != NULL) {
-                        loadOutChannels(value + 1, profile);
+                        profile->loadOutChannels(value + 1);
                     }
                 }
                 if (((profile->mSamplingRates[0] == 0) &&
@@ -2160,6 +2721,7 @@
                                     mPrimaryOutput, output);
                             mpClientInterface->closeOutput(output);
                             mOutputs.removeItem(output);
+                            nextAudioPortGeneration();
                             output = 0;
                         }
                     }
@@ -2199,7 +2761,7 @@
             }
             for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
             {
-                IOProfile *profile = mHwModules[i]->mOutputProfiles[j];
+                sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j];
                 if (profile->mSupportedDevices.types() & device) {
                     ALOGV("checkOutputsForDevice(): "
                             "clearing direct output profile %zu on module %zu", j, i);
@@ -2239,7 +2801,7 @@
         }
 
         // then look for input profiles that can be routed to this device
-        SortedVector<IOProfile *> profiles;
+        SortedVector< sp<IOProfile> > profiles;
         for (size_t module_idx = 0; module_idx < mHwModules.size(); module_idx++)
         {
             if (mHwModules[module_idx]->mHandle == 0) {
@@ -2267,7 +2829,7 @@
         // query for dynamic parameters and will be closed later by setDeviceConnectionState()
         for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) {
 
-            IOProfile *profile = profiles[profile_index];
+            sp<IOProfile> profile = profiles[profile_index];
             // nothing to do if one input is already opened for this profile
             size_t input_index;
             for (input_index = 0; input_index < mInputs.size(); input_index++) {
@@ -2305,7 +2867,7 @@
                               reply.string());
                     value = strpbrk((char *)reply.string(), "=");
                     if (value != NULL) {
-                        loadSamplingRates(value + 1, profile);
+                        profile->loadSamplingRates(value + 1);
                     }
                 }
                 if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) {
@@ -2314,7 +2876,7 @@
                     ALOGV("checkInputsForDevice() direct input sup formats %s", reply.string());
                     value = strpbrk((char *)reply.string(), "=");
                     if (value != NULL) {
-                        loadFormats(value + 1, profile);
+                        profile->loadFormats(value + 1);
                     }
                 }
                 if (profile->mChannelMasks[0] == 0) {
@@ -2324,7 +2886,7 @@
                               reply.string());
                     value = strpbrk((char *)reply.string(), "=");
                     if (value != NULL) {
-                        loadInChannels(value + 1, profile);
+                        profile->loadInChannels(value + 1);
                     }
                 }
                 if (((profile->mSamplingRates[0] == 0) && (profile->mSamplingRates.size() < 2)) ||
@@ -2374,7 +2936,7 @@
             for (size_t profile_index = 0;
                  profile_index < mHwModules[module_index]->mInputProfiles.size();
                  profile_index++) {
-                IOProfile *profile = mHwModules[module_index]->mInputProfiles[profile_index];
+                sp<IOProfile> profile = mHwModules[module_index]->mInputProfiles[profile_index];
                 if (profile->mSupportedDevices.types() & device) {
                     ALOGV("checkInputsForDevice(): clearing direct input profile %d on module %d",
                           profile_index, module_index);
@@ -2446,6 +3008,7 @@
     delete outputDesc;
     mOutputs.removeItem(output);
     mPreviousOutputs = mOutputs;
+    nextAudioPortGeneration();
 }
 
 SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device,
@@ -2593,11 +3156,22 @@
     }
 }
 
-audio_devices_t AudioPolicyManager::getNewDevice(audio_io_handle_t output, bool fromCache)
+audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, bool fromCache)
 {
     audio_devices_t device = AUDIO_DEVICE_NONE;
 
     AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+
+    ssize_t index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+    if (index >= 0) {
+        sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+        if (patchDesc->mUid != mUidCached) {
+            ALOGV("getNewOutputDevice() device %08x forced by patch %d",
+                  outputDesc->device(), outputDesc->mPatchHandle);
+            return outputDesc->device();
+        }
+    }
+
     // check the following by order of priority to request a routing change if necessary:
     // 1: the strategy enforced audible is active on the output:
     //      use device for strategy enforced audible
@@ -2626,7 +3200,27 @@
         device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
     }
 
-    ALOGV("getNewDevice() selected device %x", device);
+    ALOGV("getNewOutputDevice() selected device %x", device);
+    return device;
+}
+
+audio_devices_t AudioPolicyManager::getNewInputDevice(audio_io_handle_t input)
+{
+    AudioInputDescriptor *inputDesc = mInputs.valueFor(input);
+
+    ssize_t index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+    if (index >= 0) {
+        sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+        if (patchDesc->mUid != mUidCached) {
+            ALOGV("getNewInputDevice() device %08x forced by patch %d",
+                  inputDesc->mDevice, inputDesc->mPatchHandle);
+            return inputDesc->mDevice;
+        }
+    }
+
+    audio_devices_t device = getDeviceForInputSource(inputDesc->mInputSource);
+
+    ALOGV("getNewInputDevice() selected device %x", device);
     return device;
 }
 
@@ -2635,15 +3229,22 @@
 }
 
 audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stream) {
-    audio_devices_t devices;
     // By checking the range of stream before calling getStrategy, we avoid
     // getStrategy's behavior for invalid streams.  getStrategy would do a ALOGE
     // and then return STRATEGY_MEDIA, but we want to return the empty set.
     if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_CNT) {
-        devices = AUDIO_DEVICE_NONE;
-    } else {
-        AudioPolicyManager::routing_strategy strategy = getStrategy(stream);
-        devices = getDeviceForStrategy(strategy, true /*fromCache*/);
+        return AUDIO_DEVICE_NONE;
+    }
+    audio_devices_t devices;
+    AudioPolicyManager::routing_strategy strategy = getStrategy(stream);
+    devices = getDeviceForStrategy(strategy, true /*fromCache*/);
+    SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(devices, mOutputs);
+    for (size_t i = 0; i < outputs.size(); i++) {
+        AudioOutputDescriptor *outputDesc = mOutputs.valueFor(outputs[i]);
+        if (outputDesc->isStrategyActive(strategy)) {
+            devices = outputDesc->device();
+            break;
+        }
     }
     return devices;
 }
@@ -2772,7 +3373,7 @@
             }
             device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_EARPIECE;
             if (device) break;
-            device = mDefaultOutputDevice->mType;
+            device = mDefaultOutputDevice->mDeviceType;
             if (device == AUDIO_DEVICE_NONE) {
                 ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE");
             }
@@ -2801,7 +3402,7 @@
             }
             device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER;
             if (device) break;
-            device = mDefaultOutputDevice->mType;
+            device = mDefaultOutputDevice->mDeviceType;
             if (device == AUDIO_DEVICE_NONE) {
                 ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER");
             }
@@ -2883,7 +3484,7 @@
         // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise
         device |= device2;
         if (device) break;
-        device = mDefaultOutputDevice->mType;
+        device = mDefaultOutputDevice->mDeviceType;
         if (device == AUDIO_DEVICE_NONE) {
             ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA");
         }
@@ -2969,9 +3570,9 @@
         }
         for (size_t i = 0; i < NUM_STRATEGIES; i++) {
             if (outputDesc->isStrategyActive((routing_strategy)i)) {
-                setStrategyMute((routing_strategy)i, true, outputDesc->mId);
+                setStrategyMute((routing_strategy)i, true, outputDesc->mIoHandle);
                 // do tempMute unmute after twice the mute wait time
-                setStrategyMute((routing_strategy)i, false, outputDesc->mId,
+                setStrategyMute((routing_strategy)i, false, outputDesc->mIoHandle,
                                 muteWaitMs *2, device);
             }
         }
@@ -2989,7 +3590,8 @@
 uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
                                              audio_devices_t device,
                                              bool force,
-                                             int delayMs)
+                                             int delayMs,
+                                             audio_patch_handle_t *patchHandle)
 {
     ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs);
     AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
@@ -2997,8 +3599,8 @@
     uint32_t muteWaitMs;
 
     if (outputDesc->isDuplicated()) {
-        muteWaitMs = setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
-        muteWaitMs += setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
+        muteWaitMs = setOutputDevice(outputDesc->mOutput1->mIoHandle, device, force, delayMs);
+        muteWaitMs += setOutputDevice(outputDesc->mOutput2->mIoHandle, device, force, delayMs);
         return muteWaitMs;
     }
     // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
@@ -3030,9 +3632,59 @@
     }
 
     ALOGV("setOutputDevice() changing device");
+
     // do the routing
-    param.addInt(String8(AudioParameter::keyRouting), (int)device);
-    mpClientInterface->setParameters(output, param.toString(), delayMs);
+    if (device == AUDIO_DEVICE_NONE) {
+        resetOutputDevice(output, delayMs, NULL);
+    } else {
+        DeviceVector deviceList = mAvailableOutputDevices.getDevicesFromType(device);
+        if (!deviceList.isEmpty()) {
+            struct audio_patch patch;
+            outputDesc->toAudioPortConfig(&patch.sources[0]);
+            patch.num_sources = 1;
+            patch.num_sinks = 0;
+            for (size_t i = 0; i < deviceList.size() && i < AUDIO_PATCH_PORTS_MAX; i++) {
+                deviceList.itemAt(i)->toAudioPortConfig(&patch.sinks[i]);
+                patch.num_sinks++;
+            }
+            ssize_t index;
+            if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
+                index = mAudioPatches.indexOfKey(*patchHandle);
+            } else {
+                index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+            }
+            sp< AudioPatch> patchDesc;
+            audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+            if (index >= 0) {
+                patchDesc = mAudioPatches.valueAt(index);
+                afPatchHandle = patchDesc->mAfPatchHandle;
+            }
+
+            status_t status = mpClientInterface->createAudioPatch(&patch,
+                                                                   &afPatchHandle,
+                                                                   delayMs);
+            ALOGV("setOutputDevice() createAudioPatch returned %d patchHandle %d"
+                    "num_sources %d num_sinks %d",
+                                       status, afPatchHandle, patch.num_sources, patch.num_sinks);
+            if (status == NO_ERROR) {
+                if (index < 0) {
+                    patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+                                               &patch, mUidCached);
+                    addAudioPatch(patchDesc->mHandle, patchDesc);
+                } else {
+                    patchDesc->mPatch = patch;
+                }
+                patchDesc->mAfPatchHandle = afPatchHandle;
+                patchDesc->mUid = mUidCached;
+                if (patchHandle) {
+                    *patchHandle = patchDesc->mHandle;
+                }
+                outputDesc->mPatchHandle = patchDesc->mHandle;
+                nextAudioPortGeneration();
+                mpClientInterface->onAudioPatchListUpdate();
+            }
+        }
+    }
 
     // update stream volumes according to new device
     applyStreamVolumes(output, device, delayMs);
@@ -3040,7 +3692,113 @@
     return muteWaitMs;
 }
 
-AudioPolicyManager::IOProfile *AudioPolicyManager::getInputProfile(audio_devices_t device,
+status_t AudioPolicyManager::resetOutputDevice(audio_io_handle_t output,
+                                               int delayMs,
+                                               audio_patch_handle_t *patchHandle)
+{
+    AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+    ssize_t index;
+    if (patchHandle) {
+        index = mAudioPatches.indexOfKey(*patchHandle);
+    } else {
+        index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+    }
+    if (index < 0) {
+        return INVALID_OPERATION;
+    }
+    sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+    status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, delayMs);
+    ALOGV("resetOutputDevice() releaseAudioPatch returned %d", status);
+    outputDesc->mPatchHandle = 0;
+    removeAudioPatch(patchDesc->mHandle);
+    nextAudioPortGeneration();
+    mpClientInterface->onAudioPatchListUpdate();
+    return status;
+}
+
+status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input,
+                                            audio_devices_t device,
+                                            bool force,
+                                            audio_patch_handle_t *patchHandle)
+{
+    status_t status = NO_ERROR;
+
+    AudioInputDescriptor *inputDesc = mInputs.valueFor(input);
+    if ((device != AUDIO_DEVICE_NONE) && ((device != inputDesc->mDevice) || force)) {
+        inputDesc->mDevice = device;
+
+        DeviceVector deviceList = mAvailableInputDevices.getDevicesFromType(device);
+        if (!deviceList.isEmpty()) {
+            struct audio_patch patch;
+            inputDesc->toAudioPortConfig(&patch.sinks[0]);
+            patch.num_sinks = 1;
+            //only one input device for now
+            deviceList.itemAt(0)->toAudioPortConfig(&patch.sources[0]);
+            patch.num_sources = 1;
+            ssize_t index;
+            if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
+                index = mAudioPatches.indexOfKey(*patchHandle);
+            } else {
+                index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+            }
+            sp< AudioPatch> patchDesc;
+            audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+            if (index >= 0) {
+                patchDesc = mAudioPatches.valueAt(index);
+                afPatchHandle = patchDesc->mAfPatchHandle;
+            }
+
+            status_t status = mpClientInterface->createAudioPatch(&patch,
+                                                                  &afPatchHandle,
+                                                                  0);
+            ALOGV("setInputDevice() createAudioPatch returned %d patchHandle %d",
+                                                                          status, afPatchHandle);
+            if (status == NO_ERROR) {
+                if (index < 0) {
+                    patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+                                               &patch, mUidCached);
+                    addAudioPatch(patchDesc->mHandle, patchDesc);
+                } else {
+                    patchDesc->mPatch = patch;
+                }
+                patchDesc->mAfPatchHandle = afPatchHandle;
+                patchDesc->mUid = mUidCached;
+                if (patchHandle) {
+                    *patchHandle = patchDesc->mHandle;
+                }
+                inputDesc->mPatchHandle = patchDesc->mHandle;
+                nextAudioPortGeneration();
+                mpClientInterface->onAudioPatchListUpdate();
+            }
+        }
+    }
+    return status;
+}
+
+status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input,
+                                              audio_patch_handle_t *patchHandle)
+{
+    AudioInputDescriptor *inputDesc = mInputs.valueFor(input);
+    ssize_t index;
+    if (patchHandle) {
+        index = mAudioPatches.indexOfKey(*patchHandle);
+    } else {
+        index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+    }
+    if (index < 0) {
+        return INVALID_OPERATION;
+    }
+    sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+    status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+    ALOGV("resetInputDevice() releaseAudioPatch returned %d", status);
+    inputDesc->mPatchHandle = 0;
+    removeAudioPatch(patchDesc->mHandle);
+    nextAudioPortGeneration();
+    mpClientInterface->onAudioPatchListUpdate();
+    return status;
+}
+
+sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device,
                                                    uint32_t samplingRate,
                                                    audio_format_t format,
                                                    audio_channel_mask_t channelMask)
@@ -3055,7 +3813,7 @@
         }
         for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++)
         {
-            IOProfile *profile = mHwModules[i]->mInputProfiles[j];
+            sp<IOProfile> profile = mHwModules[i]->mInputProfiles[j];
             // profile->log();
             if (profile->isCompatibleProfile(device, samplingRate, format,
                                              channelMask, AUDIO_OUTPUT_FLAG_NONE)) {
@@ -3081,6 +3839,12 @@
 
     case AUDIO_SOURCE_DEFAULT:
     case AUDIO_SOURCE_MIC:
+    if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
+        device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
+        break;
+    }
+    // FALL THROUGH
+
     case AUDIO_SOURCE_VOICE_RECOGNITION:
     case AUDIO_SOURCE_HOTWORD:
     case AUDIO_SOURCE_VOICE_COMMUNICATION:
@@ -3633,13 +4397,14 @@
     return MAX_EFFECTS_MEMORY;
 }
 
+
 // --- AudioOutputDescriptor class implementation
 
 AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor(
-        const IOProfile *profile)
-    : mId(0), mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT),
+        const sp<IOProfile>& profile)
+    : mId(0), mIoHandle(0), mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT),
       mChannelMask(0), mLatency(0),
-    mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE),
+    mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPatchHandle(0),
     mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0)
 {
     // clear usage count for all stream types
@@ -3758,6 +4523,51 @@
     return false;
 }
 
+void AudioPolicyManager::AudioOutputDescriptor::toAudioPortConfig(
+                                                 struct audio_port_config *dstConfig,
+                                                 const struct audio_port_config *srcConfig) const
+{
+    dstConfig->id = mId;
+    dstConfig->role = AUDIO_PORT_ROLE_SOURCE;
+    dstConfig->type = AUDIO_PORT_TYPE_MIX;
+    dstConfig->sample_rate = mSamplingRate;
+    dstConfig->channel_mask = mChannelMask;
+    dstConfig->format = mFormat;
+    dstConfig->gain.index = -1;
+    dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
+                            AUDIO_PORT_CONFIG_FORMAT;
+    // use supplied variable configuration parameters if any
+    if (srcConfig != NULL) {
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+            dstConfig->sample_rate = srcConfig->sample_rate;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+            dstConfig->channel_mask = srcConfig->channel_mask;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+            dstConfig->format = srcConfig->format;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+            dstConfig->gain = srcConfig->gain;
+            dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+        }
+    }
+    dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle;
+    dstConfig->ext.mix.handle = mIoHandle;
+    dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
+}
+
+void AudioPolicyManager::AudioOutputDescriptor::toAudioPort(
+                                                    struct audio_port *port) const
+{
+    mProfile->toAudioPort(port);
+    port->id = mId;
+    toAudioPortConfig(&port->active_config);
+    port->ext.mix.hw_module = mProfile->mModule->mHandle;
+    port->ext.mix.handle = mIoHandle;
+    port->ext.mix.latency_class =
+            mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL;
+}
 
 status_t AudioPolicyManager::AudioOutputDescriptor::dump(int fd)
 {
@@ -3791,9 +4601,10 @@
 
 // --- AudioInputDescriptor class implementation
 
-AudioPolicyManager::AudioInputDescriptor::AudioInputDescriptor(const IOProfile *profile)
-    : mId(0), mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(0),
-      mDevice(AUDIO_DEVICE_NONE), mRefCount(0),
+AudioPolicyManager::AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile)
+    : mId(0), mIoHandle(0), mSamplingRate(0),
+      mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(0),
+      mDevice(AUDIO_DEVICE_NONE), mPatchHandle(0), mRefCount(0),
       mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile)
 {
     if (profile != NULL) {
@@ -3803,6 +4614,48 @@
     }
 }
 
+void AudioPolicyManager::AudioInputDescriptor::toAudioPortConfig(
+                                                   struct audio_port_config *dstConfig,
+                                                   const struct audio_port_config *srcConfig) const
+{
+    dstConfig->id = mId;
+    dstConfig->role = AUDIO_PORT_ROLE_SINK;
+    dstConfig->type = AUDIO_PORT_TYPE_MIX;
+    dstConfig->sample_rate = mSamplingRate;
+    dstConfig->channel_mask = mChannelMask;
+    dstConfig->format = mFormat;
+    dstConfig->gain.index = -1;
+    dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
+                            AUDIO_PORT_CONFIG_FORMAT;
+    // use supplied variable configuration parameters if any
+    if (srcConfig != NULL) {
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+            dstConfig->sample_rate = srcConfig->sample_rate;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+            dstConfig->channel_mask = srcConfig->channel_mask;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+            dstConfig->format = srcConfig->format;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+            dstConfig->gain = srcConfig->gain;
+            dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+        }
+    }
+}
+
+void AudioPolicyManager::AudioInputDescriptor::toAudioPort(
+                                                    struct audio_port *port) const
+{
+    mProfile->toAudioPort(port);
+    port->id = mId;
+    toAudioPortConfig(&port->active_config);
+    port->ext.mix.hw_module = mProfile->mModule->mHandle;
+    port->ext.mix.handle = mIoHandle;
+    port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL;
+}
+
 status_t AudioPolicyManager::AudioInputDescriptor::dump(int fd)
 {
     const size_t SIZE = 256;
@@ -3885,7 +4738,7 @@
     return NO_ERROR;
 }
 
-// --- IOProfile class implementation
+// --- HwModule class implementation
 
 AudioPolicyManager::HwModule::HwModule(const char *name)
     : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), mHandle(0)
@@ -3896,15 +4749,147 @@
 {
     for (size_t i = 0; i < mOutputProfiles.size(); i++) {
         mOutputProfiles[i]->mSupportedDevices.clear();
-        delete mOutputProfiles[i];
     }
     for (size_t i = 0; i < mInputProfiles.size(); i++) {
         mInputProfiles[i]->mSupportedDevices.clear();
-        delete mInputProfiles[i];
     }
     free((void *)mName);
 }
 
+status_t AudioPolicyManager::HwModule::loadInput(cnode *root)
+{
+    cnode *node = root->first_child;
+
+    sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this);
+
+    while (node) {
+        if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
+            profile->loadSamplingRates((char *)node->value);
+        } else if (strcmp(node->name, FORMATS_TAG) == 0) {
+            profile->loadFormats((char *)node->value);
+        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+            profile->loadInChannels((char *)node->value);
+        } else if (strcmp(node->name, DEVICES_TAG) == 0) {
+            profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+                                                           mDeclaredDevices);
+        } else if (strcmp(node->name, GAINS_TAG) == 0) {
+            profile->loadGains(node);
+        }
+        node = node->next;
+    }
+    ALOGW_IF(profile->mSupportedDevices.isEmpty(),
+            "loadInput() invalid supported devices");
+    ALOGW_IF(profile->mChannelMasks.size() == 0,
+            "loadInput() invalid supported channel masks");
+    ALOGW_IF(profile->mSamplingRates.size() == 0,
+            "loadInput() invalid supported sampling rates");
+    ALOGW_IF(profile->mFormats.size() == 0,
+            "loadInput() invalid supported formats");
+    if (!profile->mSupportedDevices.isEmpty() &&
+            (profile->mChannelMasks.size() != 0) &&
+            (profile->mSamplingRates.size() != 0) &&
+            (profile->mFormats.size() != 0)) {
+
+        ALOGV("loadInput() adding input Supported Devices %04x",
+              profile->mSupportedDevices.types());
+
+        mInputProfiles.add(profile);
+        return NO_ERROR;
+    } else {
+        return BAD_VALUE;
+    }
+}
+
+status_t AudioPolicyManager::HwModule::loadOutput(cnode *root)
+{
+    cnode *node = root->first_child;
+
+    sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this);
+
+    while (node) {
+        if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
+            profile->loadSamplingRates((char *)node->value);
+        } else if (strcmp(node->name, FORMATS_TAG) == 0) {
+            profile->loadFormats((char *)node->value);
+        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+            profile->loadOutChannels((char *)node->value);
+        } else if (strcmp(node->name, DEVICES_TAG) == 0) {
+            profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+                                                           mDeclaredDevices);
+        } else if (strcmp(node->name, FLAGS_TAG) == 0) {
+            profile->mFlags = parseFlagNames((char *)node->value);
+        } else if (strcmp(node->name, GAINS_TAG) == 0) {
+            profile->loadGains(node);
+        }
+        node = node->next;
+    }
+    ALOGW_IF(profile->mSupportedDevices.isEmpty(),
+            "loadOutput() invalid supported devices");
+    ALOGW_IF(profile->mChannelMasks.size() == 0,
+            "loadOutput() invalid supported channel masks");
+    ALOGW_IF(profile->mSamplingRates.size() == 0,
+            "loadOutput() invalid supported sampling rates");
+    ALOGW_IF(profile->mFormats.size() == 0,
+            "loadOutput() invalid supported formats");
+    if (!profile->mSupportedDevices.isEmpty() &&
+            (profile->mChannelMasks.size() != 0) &&
+            (profile->mSamplingRates.size() != 0) &&
+            (profile->mFormats.size() != 0)) {
+
+        ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
+              profile->mSupportedDevices.types(), profile->mFlags);
+
+        mOutputProfiles.add(profile);
+        return NO_ERROR;
+    } else {
+        return BAD_VALUE;
+    }
+}
+
+status_t AudioPolicyManager::HwModule::loadDevice(cnode *root)
+{
+    cnode *node = root->first_child;
+
+    audio_devices_t type = AUDIO_DEVICE_NONE;
+    while (node) {
+        if (strcmp(node->name, DEVICE_TYPE) == 0) {
+            type = parseDeviceNames((char *)node->value);
+            break;
+        }
+        node = node->next;
+    }
+    if (type == AUDIO_DEVICE_NONE ||
+            (!audio_is_input_device(type) && !audio_is_output_device(type))) {
+        ALOGW("loadDevice() bad type %08x", type);
+        return BAD_VALUE;
+    }
+    sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type);
+    deviceDesc->mModule = this;
+
+    node = root->first_child;
+    while (node) {
+        if (strcmp(node->name, DEVICE_ADDRESS) == 0) {
+            deviceDesc->mAddress = String8((char *)node->value);
+        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+            if (audio_is_input_device(type)) {
+                deviceDesc->loadInChannels((char *)node->value);
+            } else {
+                deviceDesc->loadOutChannels((char *)node->value);
+            }
+        } else if (strcmp(node->name, GAINS_TAG) == 0) {
+            deviceDesc->loadGains(node);
+        }
+        node = node->next;
+    }
+
+    ALOGV("loadDevice() adding device name %s type %08x address %s",
+          deviceDesc->mName.string(), type, deviceDesc->mAddress.string());
+
+    mDeclaredDevices.add(deviceDesc);
+
+    return NO_ERROR;
+}
+
 void AudioPolicyManager::HwModule::dump(int fd)
 {
     const size_t SIZE = 256;
@@ -3932,10 +4917,304 @@
             mInputProfiles[i]->dump(fd);
         }
     }
+    if (mDeclaredDevices.size()) {
+        write(fd, "  - devices:\n", strlen("  - devices:\n"));
+        for (size_t i = 0; i < mDeclaredDevices.size(); i++) {
+            mDeclaredDevices[i]->dump(fd, 4, i);
+        }
+    }
 }
 
-AudioPolicyManager::IOProfile::IOProfile(HwModule *module)
-    : mFlags((audio_output_flags_t)0), mModule(module)
+// --- AudioPort class implementation
+
+void AudioPolicyManager::AudioPort::toAudioPort(struct audio_port *port) const
+{
+    port->role = mRole;
+    port->type = mType;
+    unsigned int i;
+    for (i = 0; i < mSamplingRates.size() && i < AUDIO_PORT_MAX_SAMPLING_RATES; i++) {
+        port->sample_rates[i] = mSamplingRates[i];
+    }
+    port->num_sample_rates = i;
+    for (i = 0; i < mChannelMasks.size() && i < AUDIO_PORT_MAX_CHANNEL_MASKS; i++) {
+        port->channel_masks[i] = mChannelMasks[i];
+    }
+    port->num_channel_masks = i;
+    for (i = 0; i < mFormats.size() && i < AUDIO_PORT_MAX_FORMATS; i++) {
+        port->formats[i] = mFormats[i];
+    }
+    port->num_formats = i;
+
+    ALOGV("AudioPort::toAudioPort() num gains %d", mGains.size());
+
+    for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) {
+        port->gains[i] = mGains[i]->mGain;
+    }
+    port->num_gains = i;
+}
+
+
+void AudioPolicyManager::AudioPort::loadSamplingRates(char *name)
+{
+    char *str = strtok(name, "|");
+
+    // by convention, "0' in the first entry in mSamplingRates indicates the supported sampling
+    // rates should be read from the output stream after it is opened for the first time
+    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
+        mSamplingRates.add(0);
+        return;
+    }
+
+    while (str != NULL) {
+        uint32_t rate = atoi(str);
+        if (rate != 0) {
+            ALOGV("loadSamplingRates() adding rate %d", rate);
+            mSamplingRates.add(rate);
+        }
+        str = strtok(NULL, "|");
+    }
+}
+
+void AudioPolicyManager::AudioPort::loadFormats(char *name)
+{
+    char *str = strtok(name, "|");
+
+    // by convention, "0' in the first entry in mFormats indicates the supported formats
+    // should be read from the output stream after it is opened for the first time
+    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
+        mFormats.add(AUDIO_FORMAT_DEFAULT);
+        return;
+    }
+
+    while (str != NULL) {
+        audio_format_t format = (audio_format_t)stringToEnum(sFormatNameToEnumTable,
+                                                             ARRAY_SIZE(sFormatNameToEnumTable),
+                                                             str);
+        if (format != AUDIO_FORMAT_DEFAULT) {
+            mFormats.add(format);
+        }
+        str = strtok(NULL, "|");
+    }
+}
+
+void AudioPolicyManager::AudioPort::loadInChannels(char *name)
+{
+    const char *str = strtok(name, "|");
+
+    ALOGV("loadInChannels() %s", name);
+
+    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
+        mChannelMasks.add(0);
+        return;
+    }
+
+    while (str != NULL) {
+        audio_channel_mask_t channelMask =
+                (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable,
+                                                   ARRAY_SIZE(sInChannelsNameToEnumTable),
+                                                   str);
+        if (channelMask != 0) {
+            ALOGV("loadInChannels() adding channelMask %04x", channelMask);
+            mChannelMasks.add(channelMask);
+        }
+        str = strtok(NULL, "|");
+    }
+}
+
+void AudioPolicyManager::AudioPort::loadOutChannels(char *name)
+{
+    const char *str = strtok(name, "|");
+
+    ALOGV("loadOutChannels() %s", name);
+
+    // by convention, "0' in the first entry in mChannelMasks indicates the supported channel
+    // masks should be read from the output stream after it is opened for the first time
+    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
+        mChannelMasks.add(0);
+        return;
+    }
+
+    while (str != NULL) {
+        audio_channel_mask_t channelMask =
+                (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable,
+                                                   ARRAY_SIZE(sOutChannelsNameToEnumTable),
+                                                   str);
+        if (channelMask != 0) {
+            mChannelMasks.add(channelMask);
+        }
+        str = strtok(NULL, "|");
+    }
+    return;
+}
+
+audio_gain_mode_t AudioPolicyManager::AudioPort::loadGainMode(char *name)
+{
+    const char *str = strtok(name, "|");
+
+    ALOGV("loadGainMode() %s", name);
+    audio_gain_mode_t mode = 0;
+    while (str != NULL) {
+        mode |= (audio_gain_mode_t)stringToEnum(sGainModeNameToEnumTable,
+                                                ARRAY_SIZE(sGainModeNameToEnumTable),
+                                                str);
+        str = strtok(NULL, "|");
+    }
+    return mode;
+}
+
+void AudioPolicyManager::AudioPort::loadGain(cnode *root)
+{
+    cnode *node = root->first_child;
+
+    sp<AudioGain> gain = new AudioGain();
+
+    while (node) {
+        if (strcmp(node->name, GAIN_MODE) == 0) {
+            gain->mGain.mode = loadGainMode((char *)node->value);
+        } else if (strcmp(node->name, GAIN_CHANNELS) == 0) {
+            if ((mType == AUDIO_PORT_TYPE_DEVICE && mRole == AUDIO_PORT_ROLE_SOURCE) ||
+                    (mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK)) {
+                gain->mGain.channel_mask =
+                        (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable,
+                                                           ARRAY_SIZE(sInChannelsNameToEnumTable),
+                                                           (char *)node->value);
+            } else {
+                gain->mGain.channel_mask =
+                        (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable,
+                                                           ARRAY_SIZE(sOutChannelsNameToEnumTable),
+                                                           (char *)node->value);
+            }
+        } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) {
+            gain->mGain.min_value = atoi((char *)node->value);
+        } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) {
+            gain->mGain.max_value = atoi((char *)node->value);
+        } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) {
+            gain->mGain.default_value = atoi((char *)node->value);
+        } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) {
+            gain->mGain.step_value = atoi((char *)node->value);
+        } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) {
+            gain->mGain.min_ramp_ms = atoi((char *)node->value);
+        } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) {
+            gain->mGain.max_ramp_ms = atoi((char *)node->value);
+        }
+        node = node->next;
+    }
+
+    ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d",
+          gain->mGain.mode, gain->mGain.channel_mask, gain->mGain.min_value, gain->mGain.max_value);
+
+    if (gain->mGain.mode == 0) {
+        return;
+    }
+    mGains.add(gain);
+}
+
+void AudioPolicyManager::AudioPort::loadGains(cnode *root)
+{
+    cnode *node = root->first_child;
+    while (node) {
+        ALOGV("loadGains() loading gain %s", node->name);
+        loadGain(node);
+        node = node->next;
+    }
+}
+
+void AudioPolicyManager::AudioPort::dump(int fd, int spaces) const
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    if (mName.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string());
+        result.append(buffer);
+    }
+
+    if (mSamplingRates.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- sampling rates: ", spaces, "");
+        result.append(buffer);
+        for (size_t i = 0; i < mSamplingRates.size(); i++) {
+            snprintf(buffer, SIZE, "%d", mSamplingRates[i]);
+            result.append(buffer);
+            result.append(i == (mSamplingRates.size() - 1) ? "" : ", ");
+        }
+        result.append("\n");
+    }
+
+    if (mChannelMasks.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- channel masks: ", spaces, "");
+        result.append(buffer);
+        for (size_t i = 0; i < mChannelMasks.size(); i++) {
+            snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]);
+            result.append(buffer);
+            result.append(i == (mChannelMasks.size() - 1) ? "" : ", ");
+        }
+        result.append("\n");
+    }
+
+    if (mFormats.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- formats: ", spaces, "");
+        result.append(buffer);
+        for (size_t i = 0; i < mFormats.size(); i++) {
+            snprintf(buffer, SIZE, "%-48s", enumToString(sFormatNameToEnumTable,
+                                                          ARRAY_SIZE(sFormatNameToEnumTable),
+                                                          mFormats[i]));
+            result.append(buffer);
+            result.append(i == (mFormats.size() - 1) ? "" : ", ");
+        }
+        result.append("\n");
+    }
+    write(fd, result.string(), result.size());
+    if (mGains.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- gains:\n", spaces, "");
+        write(fd, buffer, strlen(buffer) + 1);
+        result.append(buffer);
+        for (size_t i = 0; i < mGains.size(); i++) {
+            mGains[i]->dump(fd, spaces + 2, i);
+        }
+    }
+}
+
+// --- AudioGain class implementation
+
+AudioPolicyManager::AudioGain::AudioGain()
+{
+    memset(&mGain, 0, sizeof(struct audio_gain));
+}
+
+void AudioPolicyManager::AudioGain::dump(int fd, int spaces, int index) const
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, "%*sGain %d:\n", spaces, "", index+1);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- mode: %08x\n", spaces, "", mGain.mode);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- channel_mask: %08x\n", spaces, "", mGain.channel_mask);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- min_value: %d mB\n", spaces, "", mGain.min_value);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- max_value: %d mB\n", spaces, "", mGain.max_value);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- default_value: %d mB\n", spaces, "", mGain.default_value);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- step_value: %d mB\n", spaces, "", mGain.step_value);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- min_ramp_ms: %d ms\n", spaces, "", mGain.min_ramp_ms);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- max_ramp_ms: %d ms\n", spaces, "", mGain.max_ramp_ms);
+    result.append(buffer);
+
+    write(fd, result.string(), result.size());
+}
+
+// --- IOProfile class implementation
+
+AudioPolicyManager::IOProfile::IOProfile(const String8& name, audio_port_role_t role,
+                                         HwModule *module)
+    : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module), mFlags((audio_output_flags_t)0)
 {
 }
 
@@ -3999,42 +5278,16 @@
     char buffer[SIZE];
     String8 result;
 
-    snprintf(buffer, SIZE, "    - sampling rates: ");
-    result.append(buffer);
-    for (size_t i = 0; i < mSamplingRates.size(); i++) {
-        snprintf(buffer, SIZE, "%d", mSamplingRates[i]);
-        result.append(buffer);
-        result.append(i == (mSamplingRates.size() - 1) ? "\n" : ", ");
-    }
-
-    snprintf(buffer, SIZE, "    - channel masks: ");
-    result.append(buffer);
-    for (size_t i = 0; i < mChannelMasks.size(); i++) {
-        snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]);
-        result.append(buffer);
-        result.append(i == (mChannelMasks.size() - 1) ? "\n" : ", ");
-    }
-
-    snprintf(buffer, SIZE, "    - formats: ");
-    result.append(buffer);
-    for (size_t i = 0; i < mFormats.size(); i++) {
-        snprintf(buffer, SIZE, "0x%08x", mFormats[i]);
-        result.append(buffer);
-        result.append(i == (mFormats.size() - 1) ? "\n" : ", ");
-    }
-
-    snprintf(buffer, SIZE, "    - devices:\n");
-    result.append(buffer);
-    write(fd, result.string(), result.size());
-    DeviceDescriptor::dumpHeader(fd, 6);
-    for (size_t i = 0; i < mSupportedDevices.size(); i++) {
-        mSupportedDevices[i]->dump(fd, 6);
-    }
+    AudioPort::dump(fd, 4);
 
     snprintf(buffer, SIZE, "    - flags: 0x%04x\n", mFlags);
     result.append(buffer);
-
+    snprintf(buffer, SIZE, "    - devices:\n");
+    result.append(buffer);
     write(fd, result.string(), result.size());
+    for (size_t i = 0; i < mSupportedDevices.size(); i++) {
+        mSupportedDevices[i]->dump(fd, 6, i);
+    }
 }
 
 void AudioPolicyManager::IOProfile::log()
@@ -4071,7 +5324,7 @@
     // - are of the same type (a device type cannot be AUDIO_DEVICE_NONE)
     // - have the same address or one device does not specify the address
     // - have the same channel mask or one device does not specify the channel mask
-    return (mType == other->mType) &&
+    return (mDeviceType == other->mDeviceType) &&
            (mAddress == "" || other->mAddress == "" || mAddress == other->mAddress) &&
            (mChannelMask == 0 || other->mChannelMask == 0 ||
                 mChannelMask == other->mChannelMask);
@@ -4079,11 +5332,11 @@
 
 void AudioPolicyManager::DeviceVector::refreshTypes()
 {
-    mTypes = AUDIO_DEVICE_NONE;
+    mDeviceTypes = AUDIO_DEVICE_NONE;
     for(size_t i = 0; i < size(); i++) {
-        mTypes |= itemAt(i)->mType;
+        mDeviceTypes |= itemAt(i)->mDeviceType;
     }
-    ALOGV("DeviceVector::refreshTypes() mTypes %08x", mTypes);
+    ALOGV("DeviceVector::refreshTypes() mDeviceTypes %08x", mDeviceTypes);
 }
 
 ssize_t AudioPolicyManager::DeviceVector::indexOf(const sp<DeviceDescriptor>& item) const
@@ -4106,7 +5359,7 @@
             refreshTypes();
         }
     } else {
-        ALOGW("DeviceVector::add device %08x already in", item->mType);
+        ALOGW("DeviceVector::add device %08x already in", item->mDeviceType);
         ret = -1;
     }
     return ret;
@@ -4118,7 +5371,7 @@
     ssize_t ret = indexOf(item);
 
     if (ret < 0) {
-        ALOGW("DeviceVector::remove device %08x not in", item->mType);
+        ALOGW("DeviceVector::remove device %08x not in", item->mDeviceType);
     } else {
         ret = SortedVector::removeAt(ret);
         if (ret >= 0) {
@@ -4139,32 +5392,156 @@
         uint32_t i = 31 - __builtin_clz(types);
         uint32_t type = 1 << i;
         types &= ~type;
-        add(new DeviceDescriptor(type | role_bit));
+        add(new DeviceDescriptor(String8(""), type | role_bit));
     }
 }
 
-void AudioPolicyManager::DeviceDescriptor::dumpHeader(int fd, int spaces)
+void AudioPolicyManager::DeviceVector::loadDevicesFromName(char *name,
+                                                           const DeviceVector& declaredDevices)
 {
-    const size_t SIZE = 256;
-    char buffer[SIZE];
-
-    snprintf(buffer, SIZE, "%*s%-48s %-2s %-8s %-32s \n",
-                         spaces, "", "Type", "ID", "Cnl Mask", "Address");
-    write(fd, buffer, strlen(buffer));
+    char *devName = strtok(name, "|");
+    while (devName != NULL) {
+        if (strlen(devName) != 0) {
+            audio_devices_t type = stringToEnum(sDeviceNameToEnumTable,
+                                 ARRAY_SIZE(sDeviceNameToEnumTable),
+                                 devName);
+            if (type != AUDIO_DEVICE_NONE) {
+                add(new DeviceDescriptor(String8(""), type));
+            } else {
+                sp<DeviceDescriptor> deviceDesc =
+                        declaredDevices.getDeviceFromName(String8(devName));
+                if (deviceDesc != 0) {
+                    add(deviceDesc);
+                }
+            }
+         }
+        devName = strtok(NULL, "|");
+     }
 }
 
-status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces) const
+sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDevice(
+                                                        audio_devices_t type, String8 address) const
+{
+    sp<DeviceDescriptor> device;
+    for (size_t i = 0; i < size(); i++) {
+        if (itemAt(i)->mDeviceType == type) {
+            device = itemAt(i);
+            if (itemAt(i)->mAddress = address) {
+                break;
+            }
+        }
+    }
+    ALOGV("DeviceVector::getDevice() for type %d address %s found %p",
+          type, address.string(), device.get());
+    return device;
+}
+
+sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromId(
+                                                                    audio_port_handle_t id) const
+{
+    sp<DeviceDescriptor> device;
+    for (size_t i = 0; i < size(); i++) {
+        ALOGV("DeviceVector::getDeviceFromId(%d) itemAt(%d)->mId %d", id, i, itemAt(i)->mId);
+        if (itemAt(i)->mId == id) {
+            device = itemAt(i);
+            break;
+        }
+    }
+    return device;
+}
+
+AudioPolicyManager::DeviceVector AudioPolicyManager::DeviceVector::getDevicesFromType(
+                                                                        audio_devices_t type) const
+{
+    DeviceVector devices;
+    for (size_t i = 0; (i < size()) && (type != AUDIO_DEVICE_NONE); i++) {
+        if (itemAt(i)->mDeviceType & type & ~AUDIO_DEVICE_BIT_IN) {
+            devices.add(itemAt(i));
+            type &= ~itemAt(i)->mDeviceType;
+            ALOGV("DeviceVector::getDevicesFromType() for type %x found %p",
+                  itemAt(i)->mDeviceType, itemAt(i).get());
+        }
+    }
+    return devices;
+}
+
+sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromName(
+        const String8& name) const
+{
+    sp<DeviceDescriptor> device;
+    for (size_t i = 0; i < size(); i++) {
+        if (itemAt(i)->mName == name) {
+            device = itemAt(i);
+            break;
+        }
+    }
+    return device;
+}
+
+void AudioPolicyManager::DeviceDescriptor::toAudioPortConfig(
+                                                    struct audio_port_config *dstConfig,
+                                                    const struct audio_port_config *srcConfig) const
+{
+    dstConfig->id = mId;
+    dstConfig->role = audio_is_output_device(mDeviceType) ?
+                        AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE;
+    dstConfig->type = AUDIO_PORT_TYPE_DEVICE;
+    dstConfig->channel_mask = mChannelMask;
+    dstConfig->gain.index = -1;
+    dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK;
+    // use supplied variable configuration parameters if any
+    if (srcConfig != NULL) {
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+            dstConfig->channel_mask = srcConfig->channel_mask;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+            dstConfig->gain = srcConfig->gain;
+            dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+        }
+    }
+    dstConfig->ext.device.type = mDeviceType;
+    dstConfig->ext.device.hw_module = mModule->mHandle;
+    strncpy(dstConfig->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
+}
+
+void AudioPolicyManager::DeviceDescriptor::toAudioPort(struct audio_port *port) const
+{
+    ALOGV("DeviceVector::toAudioPort() handle %d type %x", mId, mDeviceType);
+    AudioPort::toAudioPort(port);
+    port->id = mId;
+    toAudioPortConfig(&port->active_config);
+    port->ext.device.type = mDeviceType;
+    port->ext.device.hw_module = mModule->mHandle;
+    strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
+}
+
+status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces, int index) const
 {
     const size_t SIZE = 256;
     char buffer[SIZE];
+    String8 result;
 
-    snprintf(buffer, SIZE, "%*s%-48s %2d %08x %-32s \n",
-                         spaces, "",
-                         enumToString(sDeviceNameToEnumTable,
-                                      ARRAY_SIZE(sDeviceNameToEnumTable),
-                                      mType),
-                         mId, mChannelMask, mAddress.string());
-    write(fd, buffer, strlen(buffer));
+    snprintf(buffer, SIZE, "%*sDevice %d:\n", spaces, "", index+1);
+    result.append(buffer);
+    if (mId != 0) {
+        snprintf(buffer, SIZE, "%*s- id: %2d\n", spaces, "", mId);
+        result.append(buffer);
+    }
+    snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "",
+                                              enumToString(sDeviceNameToEnumTable,
+                                                           ARRAY_SIZE(sDeviceNameToEnumTable),
+                                                           mDeviceType));
+    result.append(buffer);
+    if (mAddress.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- address: %-32s\n", spaces, "", mAddress.string());
+        result.append(buffer);
+    }
+    if (mChannelMask != AUDIO_CHANNEL_NONE) {
+        snprintf(buffer, SIZE, "%*s- channel mask: %08x\n", spaces, "", mChannelMask);
+        result.append(buffer);
+    }
+    write(fd, result.string(), result.size());
+    AudioPort::dump(fd, spaces);
 
     return NO_ERROR;
 }
@@ -4213,200 +5590,30 @@
     return device;
 }
 
-void AudioPolicyManager::loadSamplingRates(char *name, IOProfile *profile)
-{
-    char *str = strtok(name, "|");
-
-    // by convention, "0' in the first entry in mSamplingRates indicates the supported sampling
-    // rates should be read from the output stream after it is opened for the first time
-    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
-        profile->mSamplingRates.add(0);
-        return;
-    }
-
-    while (str != NULL) {
-        uint32_t rate = atoi(str);
-        if (rate != 0) {
-            ALOGV("loadSamplingRates() adding rate %d", rate);
-            profile->mSamplingRates.add(rate);
-        }
-        str = strtok(NULL, "|");
-    }
-    return;
-}
-
-void AudioPolicyManager::loadFormats(char *name, IOProfile *profile)
-{
-    char *str = strtok(name, "|");
-
-    // by convention, "0' in the first entry in mFormats indicates the supported formats
-    // should be read from the output stream after it is opened for the first time
-    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
-        profile->mFormats.add(AUDIO_FORMAT_DEFAULT);
-        return;
-    }
-
-    while (str != NULL) {
-        audio_format_t format = (audio_format_t)stringToEnum(sFormatNameToEnumTable,
-                                                             ARRAY_SIZE(sFormatNameToEnumTable),
-                                                             str);
-        if (format != AUDIO_FORMAT_DEFAULT) {
-            profile->mFormats.add(format);
-        }
-        str = strtok(NULL, "|");
-    }
-    return;
-}
-
-void AudioPolicyManager::loadInChannels(char *name, IOProfile *profile)
-{
-    const char *str = strtok(name, "|");
-
-    ALOGV("loadInChannels() %s", name);
-
-    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
-        profile->mChannelMasks.add(0);
-        return;
-    }
-
-    while (str != NULL) {
-        audio_channel_mask_t channelMask =
-                (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable,
-                                                   ARRAY_SIZE(sInChannelsNameToEnumTable),
-                                                   str);
-        if (channelMask != 0) {
-            ALOGV("loadInChannels() adding channelMask %04x", channelMask);
-            profile->mChannelMasks.add(channelMask);
-        }
-        str = strtok(NULL, "|");
-    }
-    return;
-}
-
-void AudioPolicyManager::loadOutChannels(char *name, IOProfile *profile)
-{
-    const char *str = strtok(name, "|");
-
-    ALOGV("loadOutChannels() %s", name);
-
-    // by convention, "0' in the first entry in mChannelMasks indicates the supported channel
-    // masks should be read from the output stream after it is opened for the first time
-    if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) {
-        profile->mChannelMasks.add(0);
-        return;
-    }
-
-    while (str != NULL) {
-        audio_channel_mask_t channelMask =
-                (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable,
-                                                   ARRAY_SIZE(sOutChannelsNameToEnumTable),
-                                                   str);
-        if (channelMask != 0) {
-            profile->mChannelMasks.add(channelMask);
-        }
-        str = strtok(NULL, "|");
-    }
-    return;
-}
-
-status_t AudioPolicyManager::loadInput(cnode *root, HwModule *module)
-{
-    cnode *node = root->first_child;
-
-    IOProfile *profile = new IOProfile(module);
-
-    while (node) {
-        if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
-            loadSamplingRates((char *)node->value, profile);
-        } else if (strcmp(node->name, FORMATS_TAG) == 0) {
-            loadFormats((char *)node->value, profile);
-        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
-            loadInChannels((char *)node->value, profile);
-        } else if (strcmp(node->name, DEVICES_TAG) == 0) {
-            profile->mSupportedDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
-        }
-        node = node->next;
-    }
-    ALOGW_IF(profile->mSupportedDevices.isEmpty(),
-            "loadInput() invalid supported devices");
-    ALOGW_IF(profile->mChannelMasks.size() == 0,
-            "loadInput() invalid supported channel masks");
-    ALOGW_IF(profile->mSamplingRates.size() == 0,
-            "loadInput() invalid supported sampling rates");
-    ALOGW_IF(profile->mFormats.size() == 0,
-            "loadInput() invalid supported formats");
-    if (!profile->mSupportedDevices.isEmpty() &&
-            (profile->mChannelMasks.size() != 0) &&
-            (profile->mSamplingRates.size() != 0) &&
-            (profile->mFormats.size() != 0)) {
-
-        ALOGV("loadInput() adding input Supported Devices %04x",
-              profile->mSupportedDevices.types());
-
-        module->mInputProfiles.add(profile);
-        return NO_ERROR;
-    } else {
-        delete profile;
-        return BAD_VALUE;
-    }
-}
-
-status_t AudioPolicyManager::loadOutput(cnode *root, HwModule *module)
-{
-    cnode *node = root->first_child;
-
-    IOProfile *profile = new IOProfile(module);
-
-    while (node) {
-        if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
-            loadSamplingRates((char *)node->value, profile);
-        } else if (strcmp(node->name, FORMATS_TAG) == 0) {
-            loadFormats((char *)node->value, profile);
-        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
-            loadOutChannels((char *)node->value, profile);
-        } else if (strcmp(node->name, DEVICES_TAG) == 0) {
-            profile->mSupportedDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
-        } else if (strcmp(node->name, FLAGS_TAG) == 0) {
-            profile->mFlags = parseFlagNames((char *)node->value);
-        }
-        node = node->next;
-    }
-    ALOGW_IF(profile->mSupportedDevices.isEmpty(),
-            "loadOutput() invalid supported devices");
-    ALOGW_IF(profile->mChannelMasks.size() == 0,
-            "loadOutput() invalid supported channel masks");
-    ALOGW_IF(profile->mSamplingRates.size() == 0,
-            "loadOutput() invalid supported sampling rates");
-    ALOGW_IF(profile->mFormats.size() == 0,
-            "loadOutput() invalid supported formats");
-    if (!profile->mSupportedDevices.isEmpty() &&
-            (profile->mChannelMasks.size() != 0) &&
-            (profile->mSamplingRates.size() != 0) &&
-            (profile->mFormats.size() != 0)) {
-
-        ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
-              profile->mSupportedDevices.types(), profile->mFlags);
-
-        module->mOutputProfiles.add(profile);
-        return NO_ERROR;
-    } else {
-        delete profile;
-        return BAD_VALUE;
-    }
-}
-
 void AudioPolicyManager::loadHwModule(cnode *root)
 {
-    cnode *node = config_find(root, OUTPUTS_TAG);
     status_t status = NAME_NOT_FOUND;
-
+    cnode *node;
     HwModule *module = new HwModule(root->name);
 
+    node = config_find(root, DEVICES_TAG);
+    if (node != NULL) {
+        node = node->first_child;
+        while (node) {
+            ALOGV("loadHwModule() loading device %s", node->name);
+            status_t tmpStatus = module->loadDevice(node);
+            if (status == NAME_NOT_FOUND || status == NO_ERROR) {
+                status = tmpStatus;
+            }
+            node = node->next;
+        }
+    }
+    node = config_find(root, OUTPUTS_TAG);
     if (node != NULL) {
         node = node->first_child;
         while (node) {
             ALOGV("loadHwModule() loading output %s", node->name);
-            status_t tmpStatus = loadOutput(node, module);
+            status_t tmpStatus = module->loadOutput(node);
             if (status == NAME_NOT_FOUND || status == NO_ERROR) {
                 status = tmpStatus;
             }
@@ -4418,13 +5625,15 @@
         node = node->first_child;
         while (node) {
             ALOGV("loadHwModule() loading input %s", node->name);
-            status_t tmpStatus = loadInput(node, module);
+            status_t tmpStatus = module->loadInput(node);
             if (status == NAME_NOT_FOUND || status == NO_ERROR) {
                 status = tmpStatus;
             }
             node = node->next;
         }
     }
+    loadGlobalConfig(root, module);
+
     if (status == NO_ERROR) {
         mHwModules.add(module);
     } else {
@@ -4447,16 +5656,22 @@
     }
 }
 
-void AudioPolicyManager::loadGlobalConfig(cnode *root)
+void AudioPolicyManager::loadGlobalConfig(cnode *root, HwModule *module)
 {
     cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
     if (node == NULL) {
         return;
     }
+    DeviceVector declaredDevices;
+    if (module != NULL) {
+        declaredDevices = module->mDeclaredDevices;
+    }
+
     node = node->first_child;
     while (node) {
         if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) {
-            mAvailableOutputDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
+            mAvailableOutputDevices.loadDevicesFromName((char *)node->value,
+                                                        declaredDevices);
             ALOGV("loadGlobalConfig() Attached Output Devices %08x",
                   mAvailableOutputDevices.types());
         } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
@@ -4464,13 +5679,14 @@
                                               ARRAY_SIZE(sDeviceNameToEnumTable),
                                               (char *)node->value);
             if (device != AUDIO_DEVICE_NONE) {
-                mDefaultOutputDevice = new DeviceDescriptor(device);
+                mDefaultOutputDevice = new DeviceDescriptor(String8(""), device);
             } else {
                 ALOGW("loadGlobalConfig() default device not specified");
             }
-            ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", mDefaultOutputDevice->mType);
+            ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", mDefaultOutputDevice->mDeviceType);
         } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) {
-            mAvailableInputDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
+            mAvailableInputDevices.loadDevicesFromName((char *)node->value,
+                                                       declaredDevices);
             ALOGV("loadGlobalConfig() Available InputDevices %08x", mAvailableInputDevices.types());
         } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
             mSpeakerDrcEnabled = stringToBool((char *)node->value);
@@ -4492,9 +5708,9 @@
     root = config_node("", "");
     config_load(root, data);
 
-    loadGlobalConfig(root);
     loadHwModules(root);
-
+    // legacy audio_policy.conf files have one global_configuration section
+    loadGlobalConfig(root, getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY));
     config_free(root);
     free(root);
     free(data);
@@ -4507,14 +5723,14 @@
 void AudioPolicyManager::defaultAudioPolicyConfig(void)
 {
     HwModule *module;
-    IOProfile *profile;
-    sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC);
+    sp<IOProfile> profile;
+    sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(String8(""), AUDIO_DEVICE_IN_BUILTIN_MIC);
     mAvailableOutputDevices.add(mDefaultOutputDevice);
     mAvailableInputDevices.add(defaultInputDevice);
 
     module = new HwModule("primary");
 
-    profile = new IOProfile(module);
+    profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SOURCE, module);
     profile->mSamplingRates.add(44100);
     profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
     profile->mChannelMasks.add(AUDIO_CHANNEL_OUT_STEREO);
@@ -4522,7 +5738,7 @@
     profile->mFlags = AUDIO_OUTPUT_FLAG_PRIMARY;
     module->mOutputProfiles.add(profile);
 
-    profile = new IOProfile(module);
+    profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SINK, module);
     profile->mSamplingRates.add(8000);
     profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
     profile->mChannelMasks.add(AUDIO_CHANNEL_IN_MONO);
diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h
index f00fa8a..e012d63 100644
--- a/services/audiopolicy/AudioPolicyManager.h
+++ b/services/audiopolicy/AudioPolicyManager.h
@@ -140,6 +140,23 @@
 
         virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo);
 
+        virtual status_t listAudioPorts(audio_port_role_t role,
+                                        audio_port_type_t type,
+                                        unsigned int *num_ports,
+                                        struct audio_port *ports,
+                                        unsigned int *generation);
+        virtual status_t getAudioPort(struct audio_port *port);
+        virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                           audio_patch_handle_t *handle,
+                                           uid_t uid);
+        virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+                                              uid_t uid);
+        virtual status_t listAudioPatches(unsigned int *num_patches,
+                                          struct audio_patch *patches,
+                                          unsigned int *generation);
+        virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+        virtual void clearAudioPatches(uid_t uid);
+
 protected:
 
         enum routing_strategy {
@@ -173,60 +190,123 @@
             DEVICE_CATEGORY_CNT
         };
 
-        class IOProfile;
+        class HwModule;
 
-        class DeviceDescriptor: public RefBase
+        class AudioGain: public RefBase
         {
         public:
-            DeviceDescriptor(audio_devices_t type, String8 address,
+            AudioGain();
+            virtual ~AudioGain() {}
+
+            void dump(int fd, int spaces, int index) const;
+
+            struct audio_gain mGain;
+        };
+
+        class AudioPort: public RefBase
+        {
+        public:
+            AudioPort(const String8& name, audio_port_type_t type,
+                      audio_port_role_t role, HwModule *module) :
+                mName(name), mType(type), mRole(role), mModule(module) {}
+            virtual ~AudioPort() {}
+
+            virtual void toAudioPort(struct audio_port *port) const;
+
+            void loadSamplingRates(char *name);
+            void loadFormats(char *name);
+            void loadOutChannels(char *name);
+            void loadInChannels(char *name);
+
+            audio_gain_mode_t loadGainMode(char *name);
+            void loadGain(cnode *root);
+            void loadGains(cnode *root);
+
+            void dump(int fd, int spaces) const;
+
+            String8           mName;
+            audio_port_type_t mType;
+            audio_port_role_t mRole;
+            // by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats
+            // indicates the supported parameters should be read from the output stream
+            // after it is opened for the first time
+            Vector <uint32_t> mSamplingRates; // supported sampling rates
+            Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks
+            Vector <audio_format_t> mFormats; // supported audio formats
+            Vector < sp<AudioGain> > mGains; // gain controllers
+            HwModule *mModule;                 // audio HW module exposing this I/O stream
+        };
+
+        class AudioPatch: public RefBase
+        {
+        public:
+            AudioPatch(audio_patch_handle_t handle,
+                       const struct audio_patch *patch, uid_t uid) :
+                           mHandle(handle), mPatch(*patch), mUid(uid), mAfPatchHandle(0) {}
+
+            audio_patch_handle_t mHandle;
+            struct audio_patch mPatch;
+            uid_t mUid;
+            audio_patch_handle_t mAfPatchHandle;
+        };
+
+        class DeviceDescriptor: public AudioPort
+        {
+        public:
+            DeviceDescriptor(const String8& name, audio_devices_t type, String8 address,
                              audio_channel_mask_t channelMask) :
-                                 mType(type), mAddress(address),
+                                 AudioPort(name, AUDIO_PORT_TYPE_DEVICE,
+                                           audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK :
+                                                                          AUDIO_PORT_ROLE_SOURCE,
+                                         NULL),
+                                 mDeviceType(type), mAddress(address),
                                  mChannelMask(channelMask), mId(0) {}
 
-            DeviceDescriptor(audio_devices_t type) :
-                                 mType(type), mAddress(""),
-                                 mChannelMask(0), mId(0) {}
-
-            status_t dump(int fd, int spaces) const;
-            static void dumpHeader(int fd, int spaces);
+            DeviceDescriptor(String8 name, audio_devices_t type) :
+                                AudioPort(name, AUDIO_PORT_TYPE_DEVICE,
+                                          audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK :
+                                                                         AUDIO_PORT_ROLE_SOURCE,
+                                        NULL),
+                                mDeviceType(type), mAddress(""),
+                                mChannelMask(0), mId(0) {}
+            virtual ~DeviceDescriptor() {}
 
             bool equals(const sp<DeviceDescriptor>& other) const;
+            void toAudioPortConfig(struct audio_port_config *dstConfig,
+                                   const struct audio_port_config *srcConfig = NULL) const;
 
-            audio_devices_t mType;
+            virtual void toAudioPort(struct audio_port *port) const;
+
+            status_t dump(int fd, int spaces, int index) const;
+
+            audio_devices_t mDeviceType;
             String8 mAddress;
             audio_channel_mask_t mChannelMask;
-            uint32_t mId;
+            audio_port_handle_t mId;
         };
 
         class DeviceVector : public SortedVector< sp<DeviceDescriptor> >
         {
         public:
-            DeviceVector() : SortedVector(), mTypes(AUDIO_DEVICE_NONE) {}
+            DeviceVector() : SortedVector(), mDeviceTypes(AUDIO_DEVICE_NONE) {}
 
             ssize_t         add(const sp<DeviceDescriptor>& item);
             ssize_t         remove(const sp<DeviceDescriptor>& item);
             ssize_t         indexOf(const sp<DeviceDescriptor>& item) const;
 
-            audio_devices_t types() const { return mTypes; }
+            audio_devices_t types() const { return mDeviceTypes; }
 
             void loadDevicesFromType(audio_devices_t types);
+            void loadDevicesFromName(char *name, const DeviceVector& declaredDevices);
+
+            sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const;
+            DeviceVector getDevicesFromType(audio_devices_t types) const;
+            sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const;
+            sp<DeviceDescriptor> getDeviceFromName(const String8& name) const;
 
         private:
             void refreshTypes();
-            audio_devices_t mTypes;
-        };
-
-        class HwModule {
-        public:
-                    HwModule(const char *name);
-                    ~HwModule();
-
-            void dump(int fd);
-
-            const char *const mName; // base name of the audio HW module (primary, a2dp ...)
-            audio_module_handle_t mHandle;
-            Vector <IOProfile *> mOutputProfiles; // output profiles exposed by this module
-            Vector <IOProfile *> mInputProfiles;  // input profiles exposed by this module
+            audio_devices_t mDeviceTypes;
         };
 
         // the IOProfile class describes the capabilities of an output or input stream.
@@ -234,11 +314,11 @@
         // It is used by the policy manager to determine if an output or input is suitable for
         // a given use case,  open/close it accordingly and connect/disconnect audio tracks
         // to/from it.
-        class IOProfile
+        class IOProfile : public AudioPort
         {
         public:
-            IOProfile(HwModule *module);
-            ~IOProfile();
+            IOProfile(const String8& name, audio_port_role_t role, HwModule *module);
+            virtual ~IOProfile();
 
             bool isCompatibleProfile(audio_devices_t device,
                                      uint32_t samplingRate,
@@ -249,17 +329,29 @@
             void dump(int fd);
             void log();
 
-            // by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats
-            // indicates the supported parameters should be read from the output stream
-            // after it is opened for the first time
-            Vector <uint32_t> mSamplingRates; // supported sampling rates
-            Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks
-            Vector <audio_format_t> mFormats; // supported audio formats
             DeviceVector  mSupportedDevices; // supported devices
                                              // (devices this output can be routed to)
             audio_output_flags_t mFlags; // attribute flags (e.g primary output,
                                                 // direct output...). For outputs only.
-            HwModule *mModule;                     // audio HW module exposing this I/O stream
+        };
+
+        class HwModule {
+        public:
+                    HwModule(const char *name);
+                    ~HwModule();
+
+            status_t loadOutput(cnode *root);
+            status_t loadInput(cnode *root);
+            status_t loadDevice(cnode *root);
+
+            void dump(int fd);
+
+            const char *const mName; // base name of the audio HW module (primary, a2dp ...)
+            audio_module_handle_t mHandle;
+            Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module
+            Vector < sp<IOProfile> > mInputProfiles;  // input profiles exposed by this module
+            DeviceVector             mDeclaredDevices; // devices declared in audio_policy.conf
+
         };
 
         // default volume curve
@@ -284,7 +376,7 @@
         class AudioOutputDescriptor
         {
         public:
-            AudioOutputDescriptor(const IOProfile *profile);
+            AudioOutputDescriptor(const sp<IOProfile>& profile);
 
             status_t    dump(int fd);
 
@@ -303,20 +395,26 @@
                              uint32_t inPastMs = 0,
                              nsecs_t sysTime = 0) const;
 
-            audio_io_handle_t mId;              // output handle
+            void toAudioPortConfig(struct audio_port_config *dstConfig,
+                                   const struct audio_port_config *srcConfig = NULL) const;
+            void toAudioPort(struct audio_port *port) const;
+
+            audio_port_handle_t mId;
+            audio_io_handle_t mIoHandle;              // output handle
             uint32_t mSamplingRate;             //
             audio_format_t mFormat;             //
             audio_channel_mask_t mChannelMask;     // output configuration
             uint32_t mLatency;                  //
             audio_output_flags_t mFlags;   //
             audio_devices_t mDevice;                   // current device this output is routed to
+            audio_patch_handle_t mPatchHandle;
             uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output
             nsecs_t mStopTime[AUDIO_STREAM_CNT];
             AudioOutputDescriptor *mOutput1;    // used by duplicated outputs: first output
             AudioOutputDescriptor *mOutput2;    // used by duplicated outputs: second output
             float mCurVolume[AUDIO_STREAM_CNT];   // current stream volume
             int mMuteCount[AUDIO_STREAM_CNT];     // mute request counter
-            const IOProfile *mProfile;          // I/O profile this output derives from
+            const sp<IOProfile> mProfile;          // I/O profile this output derives from
             bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible
                                                 // device selection. See checkDeviceMuteStrategies()
             uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only)
@@ -327,18 +425,24 @@
         class AudioInputDescriptor
         {
         public:
-            AudioInputDescriptor(const IOProfile *profile);
+            AudioInputDescriptor(const sp<IOProfile>& profile);
 
             status_t    dump(int fd);
 
-            audio_io_handle_t mId;                      // input handle
+            audio_port_handle_t mId;
+            audio_io_handle_t mIoHandle;              // input handle
             uint32_t mSamplingRate;                     //
             audio_format_t mFormat;                     // input configuration
             audio_channel_mask_t mChannelMask;             //
             audio_devices_t mDevice;                    // current device this input is routed to
+            audio_patch_handle_t mPatchHandle;
             uint32_t mRefCount;                         // number of AudioRecord clients using this output
             audio_source_t mInputSource;                // input source selected by application (mediarecorder.h)
-            const IOProfile *mProfile;                  // I/O profile this output derives from
+            const sp<IOProfile> mProfile;                  // I/O profile this output derives from
+
+            void toAudioPortConfig(struct audio_port_config *dstConfig,
+                                   const struct audio_port_config *srcConfig = NULL) const;
+            void toAudioPort(struct audio_port *port) const;
         };
 
         // stream descriptor used for volume control
@@ -372,8 +476,8 @@
             bool mEnabled;              // enabled state: CPU load being used or not
         };
 
-        void addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc);
-        void addInput(audio_io_handle_t id, AudioInputDescriptor *inputDesc);
+        void addOutput(audio_io_handle_t output, AudioOutputDescriptor *outputDesc);
+        void addInput(audio_io_handle_t input, AudioInputDescriptor *inputDesc);
 
         // return the strategy corresponding to a given stream type
         static routing_strategy getStrategy(audio_stream_type_t stream);
@@ -397,7 +501,17 @@
         uint32_t setOutputDevice(audio_io_handle_t output,
                              audio_devices_t device,
                              bool force = false,
-                             int delayMs = 0);
+                             int delayMs = 0,
+                             audio_patch_handle_t *patchHandle = NULL);
+        status_t resetOutputDevice(audio_io_handle_t output,
+                                   int delayMs = 0,
+                                   audio_patch_handle_t *patchHandle = NULL);
+        status_t setInputDevice(audio_io_handle_t input,
+                                audio_devices_t device,
+                                bool force = false,
+                                audio_patch_handle_t *patchHandle = NULL);
+        status_t resetInputDevice(audio_io_handle_t input,
+                                  audio_patch_handle_t *patchHandle = NULL);
 
         // select input device corresponding to requested audio source
         virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource);
@@ -484,16 +598,18 @@
         // must be called every time a condition that affects the device choice for a given output is
         // changed: connected device, phone state, force use, output start, output stop..
         // see getDeviceForStrategy() for the use of fromCache parameter
+        audio_devices_t getNewOutputDevice(audio_io_handle_t output, bool fromCache);
 
-        audio_devices_t getNewDevice(audio_io_handle_t output, bool fromCache);
         // updates cache of device used by all strategies (mDeviceForStrategy[])
         // must be called every time a condition that affects the device choice for a given strategy is
         // changed: connected device, phone state, force use...
         // cached values are used by getDeviceForStrategy() if parameter fromCache is true.
          // Must be called after checkOutputForAllStrategies()
-
         void updateDevicesAndOutputs();
 
+        // selects the most appropriate device on input for current state
+        audio_devices_t getNewInputDevice(audio_io_handle_t input);
+
         virtual uint32_t getMaxEffectsCpuLoad();
         virtual uint32_t getMaxEffectsMemory();
 #ifdef AUDIO_POLICY_TEST
@@ -525,11 +641,11 @@
 
         audio_io_handle_t selectOutput(const SortedVector<audio_io_handle_t>& outputs,
                                        audio_output_flags_t flags);
-        IOProfile *getInputProfile(audio_devices_t device,
+        sp<IOProfile> getInputProfile(audio_devices_t device,
                                    uint32_t samplingRate,
                                    audio_format_t format,
                                    audio_channel_mask_t channelMask);
-        IOProfile *getProfileForDirectOutput(audio_devices_t device,
+        sp<IOProfile> getProfileForDirectOutput(audio_devices_t device,
                                                        uint32_t samplingRate,
                                                        audio_format_t format,
                                                        audio_channel_mask_t channelMask,
@@ -539,6 +655,14 @@
 
         bool isNonOffloadableEffectEnabled();
 
+        status_t addAudioPatch(audio_patch_handle_t handle,
+                               const sp<AudioPatch>& patch);
+        status_t removeAudioPatch(audio_patch_handle_t handle);
+
+        AudioOutputDescriptor *getOutputFromId(audio_port_handle_t id) const;
+        AudioInputDescriptor *getInputFromId(audio_port_handle_t id) const;
+        HwModule *getModuleForDevice(audio_devices_t device) const;
+        HwModule *getModuleFromName(const char *name) const;
         //
         // Audio policy configuration file parsing (audio_policy.conf)
         //
@@ -551,19 +675,14 @@
         static bool stringToBool(const char *value);
         static audio_output_flags_t parseFlagNames(char *name);
         static audio_devices_t parseDeviceNames(char *name);
-        void loadSamplingRates(char *name, IOProfile *profile);
-        void loadFormats(char *name, IOProfile *profile);
-        void loadOutChannels(char *name, IOProfile *profile);
-        void loadInChannels(char *name, IOProfile *profile);
-        status_t loadOutput(cnode *root,  HwModule *module);
-        status_t loadInput(cnode *root,  HwModule *module);
         void loadHwModule(cnode *root);
         void loadHwModules(cnode *root);
-        void loadGlobalConfig(cnode *root);
+        void loadGlobalConfig(cnode *root, HwModule *module);
         status_t loadAudioPolicyConfig(const char *path);
         void defaultAudioPolicyConfig(void);
 
 
+        uid_t mUidCached;
         AudioPolicyClientInterface *mpClientInterface;  // audio policy client interface
         audio_io_handle_t mPrimaryOutput;              // primary output handle
         // list of descriptors for outputs currently opened
@@ -572,10 +691,8 @@
         // reset to mOutputs when updateDevicesAndOutputs() is called.
         DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mPreviousOutputs;
         DefaultKeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs;     // list of input descriptors
-        DeviceVector  mAvailableOutputDevices; // bit field of all available output devices
-        DeviceVector  mAvailableInputDevices; // bit field of all available input devices
-                                                // without AUDIO_DEVICE_BIT_IN to allow direct bit
-                                                // field comparisons
+        DeviceVector  mAvailableOutputDevices; // all available output devices
+        DeviceVector  mAvailableInputDevices;  // all available input devices
         int mPhoneState;                                                    // current phone state
         audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT];   // current forced use configuration
 
@@ -598,6 +715,9 @@
 
         Vector <HwModule *> mHwModules;
         volatile int32_t mNextUniqueId;
+        volatile int32_t mAudioPortGeneration;
+
+        DefaultKeyedVector<audio_patch_handle_t, sp<AudioPatch> > mAudioPatches;
 
 #ifdef AUDIO_POLICY_TEST
         Mutex   mLock;
@@ -622,6 +742,8 @@
         void handleNotificationRoutingForStream(audio_stream_type_t stream);
         static bool isVirtualInputDevice(audio_devices_t device);
         uint32_t nextUniqueId();
+        uint32_t nextAudioPortGeneration();
+        uint32_t curAudioPortGeneration() const { return mAudioPortGeneration; }
         // converts device address to string sent to audio HAL via setParameters
         static String8 addressToParameter(audio_devices_t device, const String8 address);
 };
diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp
index 4e9a2f0..a2a0461 100644
--- a/services/audiopolicy/AudioPolicyService.cpp
+++ b/services/audiopolicy/AudioPolicyService.cpp
@@ -148,8 +148,123 @@
     delete mAudioPolicyManager;
     delete mAudioPolicyClient;
 #endif
+
+    mNotificationClients.clear();
 }
 
+// A notification client is always registered by AudioSystem when the client process
+// connects to AudioPolicyService.
+void AudioPolicyService::registerClient(const sp<IAudioPolicyServiceClient>& client)
+{
+
+    Mutex::Autolock _l(mLock);
+
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    if (mNotificationClients.indexOfKey(uid) < 0) {
+        sp<NotificationClient> notificationClient = new NotificationClient(this,
+                                                                           client,
+                                                                           uid);
+        ALOGV("registerClient() client %p, uid %d", client.get(), uid);
+
+        mNotificationClients.add(uid, notificationClient);
+
+        sp<IBinder> binder = client->asBinder();
+        binder->linkToDeath(notificationClient);
+    }
+}
+
+// removeNotificationClient() is called when the client process dies.
+void AudioPolicyService::removeNotificationClient(uid_t uid)
+{
+    Mutex::Autolock _l(mLock);
+
+    mNotificationClients.removeItem(uid);
+
+#ifndef USE_LEGACY_AUDIO_POLICY
+        if (mAudioPolicyManager) {
+            mAudioPolicyManager->clearAudioPatches(uid);
+        }
+#endif
+}
+
+void AudioPolicyService::onAudioPortListUpdate()
+{
+    mOutputCommandThread->updateAudioPortListCommand();
+}
+
+void AudioPolicyService::doOnAudioPortListUpdate()
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mNotificationClients.size(); i++) {
+        mNotificationClients.valueAt(i)->onAudioPortListUpdate();
+    }
+}
+
+void AudioPolicyService::onAudioPatchListUpdate()
+{
+    mOutputCommandThread->updateAudioPatchListCommand();
+}
+
+status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
+                                                audio_patch_handle_t *handle,
+                                                int delayMs)
+{
+    return mAudioCommandThread->createAudioPatchCommand(patch, handle, delayMs);
+}
+
+status_t AudioPolicyService::clientReleaseAudioPatch(audio_patch_handle_t handle,
+                                                 int delayMs)
+{
+    return mAudioCommandThread->releaseAudioPatchCommand(handle, delayMs);
+}
+
+void AudioPolicyService::doOnAudioPatchListUpdate()
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mNotificationClients.size(); i++) {
+        mNotificationClients.valueAt(i)->onAudioPatchListUpdate();
+    }
+}
+
+status_t AudioPolicyService::clientSetAudioPortConfig(const struct audio_port_config *config,
+                                                      int delayMs)
+{
+    return mAudioCommandThread->setAudioPortConfigCommand(config, delayMs);
+}
+
+AudioPolicyService::NotificationClient::NotificationClient(const sp<AudioPolicyService>& service,
+                                                     const sp<IAudioPolicyServiceClient>& client,
+                                                     uid_t uid)
+    : mService(service), mUid(uid), mAudioPolicyServiceClient(client)
+{
+}
+
+AudioPolicyService::NotificationClient::~NotificationClient()
+{
+}
+
+void AudioPolicyService::NotificationClient::binderDied(const wp<IBinder>& who __unused)
+{
+    sp<NotificationClient> keep(this);
+    sp<AudioPolicyService> service = mService.promote();
+    if (service != 0) {
+        service->removeNotificationClient(mUid);
+    }
+}
+
+void AudioPolicyService::NotificationClient::onAudioPortListUpdate()
+{
+    if (mAudioPolicyServiceClient != 0) {
+        mAudioPolicyServiceClient->onAudioPortListUpdate();
+    }
+}
+
+void AudioPolicyService::NotificationClient::onAudioPatchListUpdate()
+{
+    if (mAudioPolicyServiceClient != 0) {
+        mAudioPolicyServiceClient->onAudioPatchListUpdate();
+    }
+}
 
 void AudioPolicyService::binderDied(const wp<IBinder>& who) {
     ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(),
@@ -357,6 +472,56 @@
                     svc->doReleaseOutput(data->mIO);
                     mLock.lock();
                     }break;
+                case CREATE_AUDIO_PATCH: {
+                    CreateAudioPatchData *data = (CreateAudioPatchData *)command->mParam.get();
+                    ALOGV("AudioCommandThread() processing create audio patch");
+                    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+                    if (af == 0) {
+                        command->mStatus = PERMISSION_DENIED;
+                    } else {
+                        command->mStatus = af->createAudioPatch(&data->mPatch, &data->mHandle);
+                    }
+                    } break;
+                case RELEASE_AUDIO_PATCH: {
+                    ReleaseAudioPatchData *data = (ReleaseAudioPatchData *)command->mParam.get();
+                    ALOGV("AudioCommandThread() processing release audio patch");
+                    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+                    if (af == 0) {
+                        command->mStatus = PERMISSION_DENIED;
+                    } else {
+                        command->mStatus = af->releaseAudioPatch(data->mHandle);
+                    }
+                    } break;
+                case UPDATE_AUDIOPORT_LIST: {
+                    ALOGV("AudioCommandThread() processing update audio port list");
+                    sp<AudioPolicyService> svc = mService.promote();
+                    if (svc == 0) {
+                        break;
+                    }
+                    mLock.unlock();
+                    svc->doOnAudioPortListUpdate();
+                    mLock.lock();
+                    }break;
+                case UPDATE_AUDIOPATCH_LIST: {
+                    ALOGV("AudioCommandThread() processing update audio patch list");
+                    sp<AudioPolicyService> svc = mService.promote();
+                    if (svc == 0) {
+                        break;
+                    }
+                    mLock.unlock();
+                    svc->doOnAudioPatchListUpdate();
+                    mLock.lock();
+                    }break;
+                case SET_AUDIOPORT_CONFIG: {
+                    SetAudioPortConfigData *data = (SetAudioPortConfigData *)command->mParam.get();
+                    ALOGV("AudioCommandThread() processing set port config");
+                    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+                    if (af == 0) {
+                        command->mStatus = PERMISSION_DENIED;
+                    } else {
+                        command->mStatus = af->setAudioPortConfig(&data->mConfig);
+                    }
+                    } break;
                 default:
                     ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                 }
@@ -516,6 +681,70 @@
     sendCommand(command);
 }
 
+status_t AudioPolicyService::AudioCommandThread::createAudioPatchCommand(
+                                                const struct audio_patch *patch,
+                                                audio_patch_handle_t *handle,
+                                                int delayMs)
+{
+    status_t status = NO_ERROR;
+
+    sp<AudioCommand> command = new AudioCommand();
+    command->mCommand = CREATE_AUDIO_PATCH;
+    CreateAudioPatchData *data = new CreateAudioPatchData();
+    data->mPatch = *patch;
+    data->mHandle = *handle;
+    command->mParam = data;
+    command->mWaitStatus = true;
+    ALOGV("AudioCommandThread() adding create patch delay %d", delayMs);
+    status = sendCommand(command, delayMs);
+    if (status == NO_ERROR) {
+        *handle = data->mHandle;
+    }
+    return status;
+}
+
+status_t AudioPolicyService::AudioCommandThread::releaseAudioPatchCommand(audio_patch_handle_t handle,
+                                                 int delayMs)
+{
+    sp<AudioCommand> command = new AudioCommand();
+    command->mCommand = RELEASE_AUDIO_PATCH;
+    ReleaseAudioPatchData *data = new ReleaseAudioPatchData();
+    data->mHandle = handle;
+    command->mParam = data;
+    command->mWaitStatus = true;
+    ALOGV("AudioCommandThread() adding release patch delay %d", delayMs);
+    return sendCommand(command, delayMs);
+}
+
+void AudioPolicyService::AudioCommandThread::updateAudioPortListCommand()
+{
+    sp<AudioCommand> command = new AudioCommand();
+    command->mCommand = UPDATE_AUDIOPORT_LIST;
+    ALOGV("AudioCommandThread() adding update audio port list");
+    sendCommand(command);
+}
+
+void AudioPolicyService::AudioCommandThread::updateAudioPatchListCommand()
+{
+    sp<AudioCommand>command = new AudioCommand();
+    command->mCommand = UPDATE_AUDIOPATCH_LIST;
+    ALOGV("AudioCommandThread() adding update audio patch list");
+    sendCommand(command);
+}
+
+status_t AudioPolicyService::AudioCommandThread::setAudioPortConfigCommand(
+                                            const struct audio_port_config *config, int delayMs)
+{
+    sp<AudioCommand> command = new AudioCommand();
+    command->mCommand = SET_AUDIOPORT_CONFIG;
+    SetAudioPortConfigData *data = new SetAudioPortConfigData();
+    data->mConfig = *config;
+    command->mParam = data;
+    command->mWaitStatus = true;
+    ALOGV("AudioCommandThread() adding set port config delay %d", delayMs);
+    return sendCommand(command, delayMs);
+}
+
 status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
 {
     {
diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h
index 26037e4..40f589b 100644
--- a/services/audiopolicy/AudioPolicyService.h
+++ b/services/audiopolicy/AudioPolicyService.h
@@ -140,11 +140,41 @@
     virtual status_t setVoiceVolume(float volume, int delayMs = 0);
     virtual bool isOffloadSupported(const audio_offload_info_t &config);
 
+    virtual status_t listAudioPorts(audio_port_role_t role,
+                                    audio_port_type_t type,
+                                    unsigned int *num_ports,
+                                    struct audio_port *ports,
+                                    unsigned int *generation);
+    virtual status_t getAudioPort(struct audio_port *port);
+    virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle);
+    virtual status_t releaseAudioPatch(audio_patch_handle_t handle);
+    virtual status_t listAudioPatches(unsigned int *num_patches,
+                                      struct audio_patch *patches,
+                                      unsigned int *generation);
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+
+    virtual void registerClient(const sp<IAudioPolicyServiceClient>& client);
+
             status_t doStopOutput(audio_io_handle_t output,
                                   audio_stream_type_t stream,
                                   int session = 0);
             void doReleaseOutput(audio_io_handle_t output);
 
+            status_t clientCreateAudioPatch(const struct audio_patch *patch,
+                                      audio_patch_handle_t *handle,
+                                      int delayMs);
+            status_t clientReleaseAudioPatch(audio_patch_handle_t handle,
+                                             int delayMs);
+            virtual status_t clientSetAudioPortConfig(const struct audio_port_config *config,
+                                                      int delayMs);
+
+            void removeNotificationClient(uid_t uid);
+            void onAudioPortListUpdate();
+            void doOnAudioPortListUpdate();
+            void onAudioPatchListUpdate();
+            void doOnAudioPatchListUpdate();
+
 private:
                         AudioPolicyService() ANDROID_API;
     virtual             ~AudioPolicyService();
@@ -169,7 +199,12 @@
             SET_PARAMETERS,
             SET_VOICE_VOLUME,
             STOP_OUTPUT,
-            RELEASE_OUTPUT
+            RELEASE_OUTPUT,
+            CREATE_AUDIO_PATCH,
+            RELEASE_AUDIO_PATCH,
+            UPDATE_AUDIOPORT_LIST,
+            UPDATE_AUDIOPATCH_LIST,
+            SET_AUDIOPORT_CONFIG,
         };
 
         AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -196,6 +231,16 @@
                     void        releaseOutputCommand(audio_io_handle_t output);
                     status_t    sendCommand(sp<AudioCommand>& command, int delayMs = 0);
                     void        insertCommand_l(sp<AudioCommand>& command, int delayMs = 0);
+                    status_t    createAudioPatchCommand(const struct audio_patch *patch,
+                                                        audio_patch_handle_t *handle,
+                                                        int delayMs);
+                    status_t    releaseAudioPatchCommand(audio_patch_handle_t handle,
+                                                         int delayMs);
+                    void        updateAudioPortListCommand();
+                    void        updateAudioPatchListCommand();
+                    status_t    setAudioPortConfigCommand(const struct audio_port_config *config,
+                                                          int delayMs);
+                    void        insertCommand_l(AudioCommand *command, int delayMs = 0);
 
     private:
         class AudioCommandData;
@@ -261,6 +306,22 @@
             audio_io_handle_t mIO;
         };
 
+        class CreateAudioPatchData : public AudioCommandData {
+        public:
+            struct audio_patch mPatch;
+            audio_patch_handle_t mHandle;
+        };
+
+        class ReleaseAudioPatchData : public AudioCommandData {
+        public:
+            audio_patch_handle_t mHandle;
+        };
+
+        class SetAudioPortConfigData : public AudioCommandData {
+        public:
+            struct audio_port_config mConfig;
+        };
+
         Mutex   mLock;
         Condition mWaitWorkCV;
         Vector < sp<AudioCommand> > mAudioCommands; // list of pending commands
@@ -405,10 +466,48 @@
                                          audio_io_handle_t srcOutput,
                                          audio_io_handle_t dstOutput);
 
+        /* Create a patch between several source and sink ports */
+        virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                           audio_patch_handle_t *handle,
+                                           int delayMs);
+
+        /* Release a patch */
+        virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+                                           int delayMs);
+
+        /* Set audio port configuration */
+        virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs);
+
+        virtual void onAudioPortListUpdate();
+        virtual void onAudioPatchListUpdate();
+
      private:
         AudioPolicyService *mAudioPolicyService;
     };
 
+    // --- Notification Client ---
+    class NotificationClient : public IBinder::DeathRecipient {
+    public:
+                            NotificationClient(const sp<AudioPolicyService>& service,
+                                                const sp<IAudioPolicyServiceClient>& client,
+                                                uid_t uid);
+        virtual             ~NotificationClient();
+
+                            void        onAudioPortListUpdate();
+                            void        onAudioPatchListUpdate();
+
+                // IBinder::DeathRecipient
+                virtual     void        binderDied(const wp<IBinder>& who);
+
+    private:
+                            NotificationClient(const NotificationClient&);
+                            NotificationClient& operator = (const NotificationClient&);
+
+        const wp<AudioPolicyService>        mService;
+        const uid_t                         mUid;
+        const sp<IAudioPolicyServiceClient> mAudioPolicyServiceClient;
+    };
+
     static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1];
 
     void setPreProcessorEnabled(const InputDesc *inputDesc, bool enabled);
@@ -445,6 +544,8 @@
 
     KeyedVector< audio_source_t, InputSourceDesc* > mInputSources;
     KeyedVector< audio_io_handle_t, InputDesc* > mInputs;
+
+    DefaultKeyedVector< uid_t, sp<NotificationClient> >    mNotificationClients;
 };
 
 }; // namespace android
diff --git a/services/audiopolicy/audio_policy_conf.h b/services/audiopolicy/audio_policy_conf.h
new file mode 100644
index 0000000..79f20f1
--- /dev/null
+++ b/services/audiopolicy/audio_policy_conf.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_POLICY_CONF_H
+#define ANDROID_AUDIO_POLICY_CONF_H
+
+
+/////////////////////////////////////////////////
+//      Definitions for audio policy configuration file (audio_policy.conf)
+/////////////////////////////////////////////////
+
+#define AUDIO_HARDWARE_MODULE_ID_MAX_LEN 32
+
+#define AUDIO_POLICY_CONFIG_FILE "/system/etc/audio_policy.conf"
+#define AUDIO_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_policy.conf"
+
+// global configuration
+#define GLOBAL_CONFIG_TAG "global_configuration"
+
+#define ATTACHED_OUTPUT_DEVICES_TAG "attached_output_devices"
+#define DEFAULT_OUTPUT_DEVICE_TAG "default_output_device"
+#define ATTACHED_INPUT_DEVICES_TAG "attached_input_devices"
+#define SPEAKER_DRC_ENABLED_TAG "speaker_drc_enabled"
+
+// hw modules descriptions
+#define AUDIO_HW_MODULE_TAG "audio_hw_modules"
+
+#define OUTPUTS_TAG "outputs"
+#define INPUTS_TAG "inputs"
+
+#define SAMPLING_RATES_TAG "sampling_rates"
+#define FORMATS_TAG "formats"
+#define CHANNELS_TAG "channel_masks"
+#define DEVICES_TAG "devices"
+#define FLAGS_TAG "flags"
+
+#define DYNAMIC_VALUE_TAG "dynamic" // special value for "channel_masks", "sampling_rates" and
+                                    // "formats" in outputs descriptors indicating that supported
+                                    // values should be queried after opening the output.
+
+#define DEVICES_TAG "devices"
+#define DEVICE_TYPE "type"
+#define DEVICE_ADDRESS "address"
+
+#define MIXERS_TAG "mixers"
+#define MIXER_TYPE "type"
+#define MIXER_TYPE_MUX "mux"
+#define MIXER_TYPE_MIX "mix"
+
+#define GAINS_TAG "gains"
+#define GAIN_MODE "mode"
+#define GAIN_CHANNELS "channel_mask"
+#define GAIN_MIN_VALUE "min_value_mB"
+#define GAIN_MAX_VALUE "max_value_mB"
+#define GAIN_DEFAULT_VALUE "default_value_mB"
+#define GAIN_STEP_VALUE "step_value_mB"
+#define GAIN_MIN_RAMP_MS "min_ramp_ms"
+#define GAIN_MAX_RAMP_MS "max_ramp_ms"
+
+
+
+#endif  // ANDROID_AUDIO_POLICY_CONF_H
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());
         }