Merge changes Idc9808f7,If7bc8e5b into lmp-dev

* changes:
  Fix the build after -Bsymbolic fix.
  Fix PIC relocations for non-Bsymbolic builds
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index dd13fea..627f23b 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -87,6 +87,10 @@
             node_id node, OMX_U32 portIndex, OMX_BOOL enable,
             OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) = 0;
 
+   virtual status_t configureVideoTunnelMode(
+            node_id node, OMX_U32 portIndex, OMX_BOOL tunneled,
+            OMX_U32 audioHwSync, native_handle_t **sidebandHandle) = 0;
+
     virtual status_t enableGraphicBuffers(
             node_id node, OMX_U32 port_index, OMX_BOOL enable) = 0;
 
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index e756368..2442219 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -172,7 +172,8 @@
     INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
     INVOKE_ID_SELECT_TRACK = 4,
     INVOKE_ID_UNSELECT_TRACK = 5,
-    INVOKE_ID_SET_VIDEO_SCALING_MODE = 6
+    INVOKE_ID_SET_VIDEO_SCALING_MODE = 6,
+    INVOKE_ID_GET_SELECTED_TRACK = 7
 };
 
 // Keep MEDIA_TRACK_TYPE_* in sync with MediaPlayer.java.
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 3dd34ce..eb31c77 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -234,6 +234,9 @@
     status_t setComponentRole(bool isEncoder, const char *mime);
     status_t configureCodec(const char *mime, const sp<AMessage> &msg);
 
+    status_t configureTunneledVideoPlayback(int64_t audioHwSync,
+            const sp<ANativeWindow> &nativeWindow);
+
     status_t setVideoPortFormatType(
             OMX_U32 portIndex,
             OMX_VIDEO_CODINGTYPE compressionFormat,
@@ -252,10 +255,19 @@
             int32_t width, int32_t height,
             OMX_VIDEO_CODINGTYPE compressionFormat);
 
+    typedef struct drcParams {
+        int32_t drcCut;
+        int32_t drcBoost;
+        int32_t heavyCompression;
+        int32_t targetRefLevel;
+        int32_t encodedTargetLevel;
+    } drcParams_t;
+
     status_t setupAACCodec(
             bool encoder,
             int32_t numChannels, int32_t sampleRate, int32_t bitRate,
-            int32_t aacProfile, bool isADTS, int32_t sbrMode);
+            int32_t aacProfile, bool isADTS, int32_t sbrMode,
+            int32_t maxOutputChannelCount, const drcParams_t& drc);
 
     status_t setupAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate);
 
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 4ff0d62..b87a09e 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -55,10 +55,10 @@
     struct BatteryNotifier;
 
     static sp<MediaCodec> CreateByType(
-            const sp<ALooper> &looper, const char *mime, bool encoder);
+            const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err = NULL);
 
     static sp<MediaCodec> CreateByComponentName(
-            const sp<ALooper> &looper, const char *name);
+            const sp<ALooper> &looper, const char *name, status_t *err = NULL);
 
     status_t configure(
             const sp<AMessage> &format,
@@ -223,6 +223,7 @@
     AString mComponentName;
     uint32_t mReplyID;
     uint32_t mFlags;
+    status_t mStickyError;
     sp<Surface> mNativeWindow;
     SoftwareRenderer *mSoftRenderer;
     sp<AMessage> mOutputFormat;
@@ -304,6 +305,18 @@
     void updateBatteryStat();
     bool isExecuting() const;
 
+    /* called to get the last codec error when the sticky flag is set.
+     * if no such codec error is found, returns UNKNOWN_ERROR.
+     */
+    inline status_t getStickyError() const {
+        return mStickyError != 0 ? mStickyError : UNKNOWN_ERROR;
+    }
+
+    inline void setStickyError(status_t err) {
+        mFlags |= kFlagStickyError;
+        mStickyError = err;
+    }
+
     DISALLOW_EVIL_CONSTRUCTORS(MediaCodec);
 };
 
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
index 686f286..7540e07 100644
--- a/include/media/stagefright/MediaErrors.h
+++ b/include/media/stagefright/MediaErrors.h
@@ -23,6 +23,18 @@
 namespace android {
 
 enum {
+    // status_t map for errors in the media framework
+    // OK or NO_ERROR or 0 represents no error.
+
+    // See system/core/include/utils/Errors.h
+    // System standard errors from -1 through (possibly) -133
+    //
+    // Errors with special meanings and side effects.
+    // INVALID_OPERATION:  Operation attempted in an illegal state (will try to signal to app).
+    // DEAD_OBJECT:        Signal from CodecBase to MediaCodec that MediaServer has died.
+    // NAME_NOT_FOUND:     Signal from CodecBase to MediaCodec that the component was not found.
+
+    // Media errors
     MEDIA_ERROR_BASE        = -1000,
 
     ERROR_ALREADY_CONNECTED = MEDIA_ERROR_BASE,
@@ -64,8 +76,34 @@
     // Heartbeat Error Codes
     HEARTBEAT_ERROR_BASE = -3000,
     ERROR_HEARTBEAT_TERMINATE_REQUESTED                     = HEARTBEAT_ERROR_BASE,
+
+    // NDK Error codes
+    // frameworks/av/include/ndk/NdkMediaError.h
+    // from -10000 (0xFFFFD8F0 - 0xFFFFD8EC)
+    // from -20000 (0xFFFFB1E0 - 0xFFFFB1D7)
+
+    // Codec errors are permitted from 0x80001000 through 0x9000FFFF
+    ERROR_CODEC_MAX    = (signed)0x9000FFFF,
+    ERROR_CODEC_MIN    = (signed)0x80001000,
+
+    // System unknown errors from 0x80000000 - 0x80000007 (INT32_MIN + 7)
+    // See system/core/include/utils/Errors.h
 };
 
+// action codes for MediaCodecs that tell the upper layer and application
+// the severity of any error.
+enum ActionCode {
+    ACTION_CODE_FATAL,
+    ACTION_CODE_TRANSIENT,
+    ACTION_CODE_RECOVERABLE,
+};
+
+// returns true if err is a recognized DRM error code
+static inline bool isCryptoError(status_t err) {
+    return (ERROR_DRM_RESOURCE_BUSY <= err && err <= ERROR_DRM_UNKNOWN)
+            || (ERROR_DRM_VENDOR_MIN <= err && err <= ERROR_DRM_VENDOR_MAX);
+}
+
 }  // namespace android
 
 #endif  // MEDIA_ERRORS_H_
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 5df232f..c583d32 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -54,6 +54,7 @@
     GET_GRAPHIC_BUFFER_USAGE,
     SET_INTERNAL_OPTION,
     UPDATE_GRAPHIC_BUFFER_IN_META,
+    CONFIGURE_VIDEO_TUNNEL_MODE,
 };
 
 class BpOMX : public BpInterface<IOMX> {
@@ -368,6 +369,25 @@
         return err;
     }
 
+    virtual status_t configureVideoTunnelMode(
+            node_id node, OMX_U32 portIndex, OMX_BOOL tunneled,
+            OMX_U32 audioHwSync, native_handle_t **sidebandHandle ) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeInt32((int32_t)node);
+        data.writeInt32(portIndex);
+        data.writeInt32((int32_t)tunneled);
+        data.writeInt32(audioHwSync);
+        remote()->transact(CONFIGURE_VIDEO_TUNNEL_MODE, data, &reply);
+
+        status_t err = reply.readInt32();
+        if (sidebandHandle) {
+            *sidebandHandle = (native_handle_t *)reply.readNativeHandle();
+        }
+        return err;
+    }
+
+
     virtual status_t allocateBuffer(
             node_id node, OMX_U32 port_index, size_t size,
             buffer_id *buffer, void **buffer_data) {
@@ -804,6 +824,24 @@
             return NO_ERROR;
         }
 
+        case CONFIGURE_VIDEO_TUNNEL_MODE:
+        {
+            CHECK_OMX_INTERFACE(IOMX, data, reply);
+
+            node_id node = (node_id)data.readInt32();
+            OMX_U32 port_index = data.readInt32();
+            OMX_BOOL tunneled = (OMX_BOOL)data.readInt32();
+            OMX_U32 audio_hw_sync = data.readInt32();
+
+            native_handle_t *sideband_handle;
+            status_t err = configureVideoTunnelMode(
+                    node, port_index, tunneled, audio_hw_sync, &sideband_handle);
+            reply->writeInt32(err);
+            reply->writeNativeHandle(sideband_handle);
+
+            return NO_ERROR;
+        }
+
         case ALLOC_BUFFER:
         {
             CHECK_OMX_INTERFACE(IOMX, data, reply);
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 1616448..6676461 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -228,13 +228,7 @@
     }
 
     if (mVideoTrack.mSource != NULL) {
-        sp<MetaData> meta = mVideoTrack.mSource->getFormat();
-
-        int32_t width, height;
-        CHECK(meta->findInt32(kKeyWidth, &width));
-        CHECK(meta->findInt32(kKeyHeight, &height));
-
-        notifyVideoSizeChanged(width, height);
+        notifyVideoSizeChanged(getFormat(false /* audio */));
     }
 
     notifyFlagsChanged(
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index e8431e9..c713d39 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -210,9 +210,9 @@
             int32_t height;
             if (format != NULL &&
                     format->findInt32("width", &width) && format->findInt32("height", &height)) {
-                notifyVideoSizeChanged(width, height);
+                notifyVideoSizeChanged(format);
             } else {
-                notifyVideoSizeChanged(0, 0);
+                notifyVideoSizeChanged();
             }
 
             uint32_t flags = FLAG_CAN_PAUSE;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index fe115c6..55fd708 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -427,6 +427,31 @@
             break;
         }
 
+        case kWhatGetSelectedTrack:
+        {
+            status_t err = INVALID_OPERATION;
+            if (mSource != NULL) {
+                err = OK;
+
+                int32_t type32;
+                CHECK(msg->findInt32("type", (int32_t*)&type32));
+                media_track_type type = (media_track_type)type32;
+                ssize_t selectedTrack = mSource->getSelectedTrack(type);
+
+                Parcel* reply;
+                CHECK(msg->findPointer("reply", (void**)&reply));
+                reply->writeInt32(selectedTrack);
+            }
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+            response->postReply(replyID);
+            break;
+        }
+
         case kWhatSelectTrack:
         {
             uint32_t replyID;
@@ -852,60 +877,10 @@
                     mRenderer->signalAudioSinkChanged();
                 } else {
                     // video
+                    sp<AMessage> inputFormat =
+                            mSource->getFormat(false /* audio */);
 
-                    int32_t width, height;
-                    CHECK(format->findInt32("width", &width));
-                    CHECK(format->findInt32("height", &height));
-
-                    int32_t cropLeft, cropTop, cropRight, cropBottom;
-                    CHECK(format->findRect(
-                                "crop",
-                                &cropLeft, &cropTop, &cropRight, &cropBottom));
-
-                    int32_t displayWidth = cropRight - cropLeft + 1;
-                    int32_t displayHeight = cropBottom - cropTop + 1;
-
-                    ALOGV("Video output format changed to %d x %d "
-                         "(crop: %d x %d @ (%d, %d))",
-                         width, height,
-                         displayWidth,
-                         displayHeight,
-                         cropLeft, cropTop);
-
-                    sp<AMessage> videoInputFormat =
-                        mSource->getFormat(false /* audio */);
-
-                    // Take into account sample aspect ratio if necessary:
-                    int32_t sarWidth, sarHeight;
-                    if (videoInputFormat->findInt32("sar-width", &sarWidth)
-                            && videoInputFormat->findInt32(
-                                "sar-height", &sarHeight)) {
-                        ALOGV("Sample aspect ratio %d : %d",
-                              sarWidth, sarHeight);
-
-                        displayWidth = (displayWidth * sarWidth) / sarHeight;
-
-                        ALOGV("display dimensions %d x %d",
-                              displayWidth, displayHeight);
-                    }
-
-                    int32_t rotationDegrees;
-                    if (!videoInputFormat->findInt32(
-                            "rotation-degrees", &rotationDegrees)) {
-                        rotationDegrees = 0;
-                    }
-
-                    if (rotationDegrees == 90 || rotationDegrees == 270) {
-                        notifyListener(
-                                MEDIA_SET_VIDEO_SIZE,
-                                displayHeight,
-                                displayWidth);
-                    } else {
-                        notifyListener(
-                                MEDIA_SET_VIDEO_SIZE,
-                                displayWidth,
-                                displayHeight);
-                    }
+                    updateVideoSize(inputFormat, format);
                 }
             } else if (what == Decoder::kWhatShutdownCompleted) {
                 ALOGV("%s shutdown completed", audio ? "audio" : "video");
@@ -1383,6 +1358,72 @@
     mRenderer->queueBuffer(audio, buffer, reply);
 }
 
+void NuPlayer::updateVideoSize(
+        const sp<AMessage> &inputFormat,
+        const sp<AMessage> &outputFormat) {
+    if (inputFormat == NULL) {
+        ALOGW("Unknown video size, reporting 0x0!");
+        notifyListener(MEDIA_SET_VIDEO_SIZE, 0, 0);
+        return;
+    }
+
+    int32_t displayWidth, displayHeight;
+    int32_t cropLeft, cropTop, cropRight, cropBottom;
+
+    if (outputFormat != NULL) {
+        int32_t width, height;
+        CHECK(outputFormat->findInt32("width", &width));
+        CHECK(outputFormat->findInt32("height", &height));
+
+        int32_t cropLeft, cropTop, cropRight, cropBottom;
+        CHECK(outputFormat->findRect(
+                    "crop",
+                    &cropLeft, &cropTop, &cropRight, &cropBottom));
+
+        displayWidth = cropRight - cropLeft + 1;
+        displayHeight = cropBottom - cropTop + 1;
+
+        ALOGV("Video output format changed to %d x %d "
+             "(crop: %d x %d @ (%d, %d))",
+             width, height,
+             displayWidth,
+             displayHeight,
+             cropLeft, cropTop);
+    } else {
+        CHECK(inputFormat->findInt32("width", &displayWidth));
+        CHECK(inputFormat->findInt32("height", &displayHeight));
+
+        ALOGV("Video input format %d x %d", displayWidth, displayHeight);
+    }
+
+    // Take into account sample aspect ratio if necessary:
+    int32_t sarWidth, sarHeight;
+    if (inputFormat->findInt32("sar-width", &sarWidth)
+            && inputFormat->findInt32("sar-height", &sarHeight)) {
+        ALOGV("Sample aspect ratio %d : %d", sarWidth, sarHeight);
+
+        displayWidth = (displayWidth * sarWidth) / sarHeight;
+
+        ALOGV("display dimensions %d x %d", displayWidth, displayHeight);
+    }
+
+    int32_t rotationDegrees;
+    if (!inputFormat->findInt32("rotation-degrees", &rotationDegrees)) {
+        rotationDegrees = 0;
+    }
+
+    if (rotationDegrees == 90 || rotationDegrees == 270) {
+        int32_t tmp = displayWidth;
+        displayWidth = displayHeight;
+        displayHeight = tmp;
+    }
+
+    notifyListener(
+            MEDIA_SET_VIDEO_SIZE,
+            displayWidth,
+            displayHeight);
+}
+
 void NuPlayer::notifyListener(int msg, int ext1, int ext2, const Parcel *in) {
     if (mDriver == NULL) {
         return;
@@ -1441,19 +1482,19 @@
     }
 }
 
-sp<AMessage> NuPlayer::Source::getFormat(bool audio) {
-    sp<MetaData> meta = getFormatMeta(audio);
+void NuPlayer::queueDecoderShutdown(
+        bool audio, bool video, const sp<AMessage> &reply) {
+    ALOGI("queueDecoderShutdown audio=%d, video=%d", audio, video);
 
-    if (meta == NULL) {
-        return NULL;
-    }
+    mDeferredActions.push_back(
+            new ShutdownDecoderAction(audio, video));
 
-    sp<AMessage> msg = new AMessage;
+    mDeferredActions.push_back(
+            new SimpleAction(&NuPlayer::performScanSources));
 
-    if(convertMetaDataToMessage(meta, &msg) == OK) {
-        return msg;
-    }
-    return NULL;
+    mDeferredActions.push_back(new PostMessageAction(reply));
+
+    processDeferredActions();
 }
 
 status_t NuPlayer::setVideoScalingMode(int32_t mode) {
@@ -1479,6 +1520,19 @@
     return err;
 }
 
+status_t NuPlayer::getSelectedTrack(int32_t type, Parcel* reply) const {
+    sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id());
+    msg->setPointer("reply", reply);
+    msg->setInt32("type", type);
+
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+    }
+    return err;
+}
+
 status_t NuPlayer::selectTrack(size_t trackIndex, bool select) {
     sp<AMessage> msg = new AMessage(kWhatSelectTrack, id());
     msg->setSize("trackIndex", trackIndex);
@@ -1729,11 +1783,10 @@
 
         case Source::kWhatVideoSizeChanged:
         {
-            int32_t width, height;
-            CHECK(msg->findInt32("width", &width));
-            CHECK(msg->findInt32("height", &height));
+            sp<AMessage> format;
+            CHECK(msg->findMessage("format", &format));
 
-            notifyListener(MEDIA_SET_VIDEO_SIZE, width, height);
+            updateVideoSize(format);
             break;
         }
 
@@ -1889,6 +1942,21 @@
 }
 ////////////////////////////////////////////////////////////////////////////////
 
+sp<AMessage> NuPlayer::Source::getFormat(bool audio) {
+    sp<MetaData> meta = getFormatMeta(audio);
+
+    if (meta == NULL) {
+        return NULL;
+    }
+
+    sp<AMessage> msg = new AMessage;
+
+    if(convertMetaDataToMessage(meta, &msg) == OK) {
+        return msg;
+    }
+    return NULL;
+}
+
 void NuPlayer::Source::notifyFlagsChanged(uint32_t flags) {
     sp<AMessage> notify = dupNotify();
     notify->setInt32("what", kWhatFlagsChanged);
@@ -1896,11 +1964,10 @@
     notify->post();
 }
 
-void NuPlayer::Source::notifyVideoSizeChanged(int32_t width, int32_t height) {
+void NuPlayer::Source::notifyVideoSizeChanged(const sp<AMessage> &format) {
     sp<AMessage> notify = dupNotify();
     notify->setInt32("what", kWhatVideoSizeChanged);
-    notify->setInt32("width", width);
-    notify->setInt32("height", height);
+    notify->setMessage("format", format);
     notify->post();
 }
 
@@ -1915,19 +1982,4 @@
     TRESPASS();
 }
 
-void NuPlayer::queueDecoderShutdown(
-        bool audio, bool video, const sp<AMessage> &reply) {
-    ALOGI("queueDecoderShutdown audio=%d, video=%d", audio, video);
-
-    mDeferredActions.push_back(
-            new ShutdownDecoderAction(audio, video));
-
-    mDeferredActions.push_back(
-            new SimpleAction(&NuPlayer::performScanSources));
-
-    mDeferredActions.push_back(new PostMessageAction(reply));
-
-    processDeferredActions();
-}
-
 }  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 8bcf10e..fc456a4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -63,6 +63,7 @@
 
     status_t setVideoScalingMode(int32_t mode);
     status_t getTrackInfo(Parcel* reply) const;
+    status_t getSelectedTrack(int32_t type, Parcel* reply) const;
     status_t selectTrack(size_t trackIndex, bool select);
 
 protected:
@@ -109,6 +110,7 @@
         kWhatPollDuration               = 'polD',
         kWhatSourceNotify               = 'srcN',
         kWhatGetTrackInfo               = 'gTrI',
+        kWhatGetSelectedTrack           = 'gSel',
         kWhatSelectTrack                = 'selT',
     };
 
@@ -167,6 +169,10 @@
 
     status_t instantiateDecoder(bool audio, sp<Decoder> *decoder);
 
+    void updateVideoSize(
+            const sp<AMessage> &inputFormat,
+            const sp<AMessage> &outputFormat = NULL);
+
     status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
     void renderBuffer(bool audio, const sp<AMessage> &msg);
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 4748546..e33e647 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -422,6 +422,12 @@
             return mPlayer->selectTrack(trackIndex, false /* select */);
         }
 
+        case INVOKE_ID_GET_SELECTED_TRACK:
+        {
+            int32_t type = request.readInt32();
+            return mPlayer->getSelectedTrack(type, reply);
+        }
+
         default:
         {
             return INVALID_OPERATION;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 0ec017e..74892b6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -22,6 +22,7 @@
 
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MetaData.h>
+#include <media/mediaplayer.h>
 #include <utils/Vector.h>
 
 namespace android {
@@ -86,6 +87,10 @@
         return NULL;
     }
 
+    virtual ssize_t getSelectedTrack(media_track_type /* type */) const {
+        return INVALID_OPERATION;
+    }
+
     virtual status_t selectTrack(size_t /* trackIndex */, bool /* select */) {
         return INVALID_OPERATION;
     }
@@ -110,7 +115,7 @@
     sp<AMessage> dupNotify() const { return mNotify->dup(); }
 
     void notifyFlagsChanged(uint32_t flags);
-    void notifyVideoSizeChanged(int32_t width, int32_t height);
+    void notifyVideoSizeChanged(const sp<AMessage> &format = NULL);
     void notifyPrepared(status_t err = OK);
 
 private:
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 2338b23..a911f6c 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -356,7 +356,7 @@
         {
             onConnected();
 
-            notifyVideoSizeChanged(0, 0);
+            notifyVideoSizeChanged();
 
             uint32_t flags = 0;
 
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index 28f0d50..2e9a29a 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -44,7 +44,7 @@
 }
 
 void NuPlayer::StreamingSource::prepareAsync() {
-    notifyVideoSizeChanged(0, 0);
+    notifyVideoSizeChanged();
     notifyFlagsChanged(0);
     notifyPrepared();
 }
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 3c0f6e3..ac80da2 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -51,6 +51,48 @@
 
 namespace android {
 
+// OMX errors are directly mapped into status_t range if
+// there is no corresponding MediaError status code.
+// Use the statusFromOMXError(int32_t omxError) function.
+//
+// Currently this is a direct map.
+// See frameworks/native/include/media/openmax/OMX_Core.h
+//
+// Vendor OMX errors     from 0x90000000 - 0x9000FFFF
+// Extension OMX errors  from 0x8F000000 - 0x90000000
+// Standard OMX errors   from 0x80001000 - 0x80001024 (0x80001024 current)
+//
+
+// returns true if err is a recognized OMX error code.
+// as OMX error is OMX_S32, this is an int32_t type
+static inline bool isOMXError(int32_t err) {
+    return (ERROR_CODEC_MIN <= err && err <= ERROR_CODEC_MAX);
+}
+
+// converts an OMX error to a status_t
+static inline status_t statusFromOMXError(int32_t omxError) {
+    switch (omxError) {
+    case OMX_ErrorInvalidComponentName:
+    case OMX_ErrorComponentNotFound:
+        return NAME_NOT_FOUND; // can trigger illegal argument error for provided names.
+    default:
+        return isOMXError(omxError) ? omxError : 0; // no translation required
+    }
+}
+
+// checks and converts status_t to a non-side-effect status_t
+static inline status_t makeNoSideEffectStatus(status_t err) {
+    switch (err) {
+    // the following errors have side effects and may come
+    // from other code modules. Remap for safety reasons.
+    case INVALID_OPERATION:
+    case DEAD_OBJECT:
+        return UNKNOWN_ERROR;
+    default:
+        return err;
+    }
+}
+
 template<class T>
 static void InitOMXParams(T *params) {
     params->nSize = sizeof(T);
@@ -1182,77 +1224,99 @@
         }
     }
 
-    // Always try to enable dynamic output buffers on native surface
     sp<RefBase> obj;
     int32_t haveNativeWindow = msg->findObject("native-window", &obj) &&
-            obj != NULL;
+        obj != NULL;
     mStoreMetaDataInOutputBuffers = false;
     if (video && !encoder) {
         inputFormat->setInt32("adaptive-playback", false);
     }
     if (!encoder && video && haveNativeWindow) {
-        err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexOutput, OMX_TRUE);
-        if (err != OK) {
-            ALOGE("[%s] storeMetaDataInBuffers failed w/ err %d",
-                  mComponentName.c_str(), err);
+        sp<NativeWindowWrapper> windowWrapper(
+                static_cast<NativeWindowWrapper *>(obj.get()));
+        sp<ANativeWindow> nativeWindow = windowWrapper->getNativeWindow();
 
-            // if adaptive playback has been requested, try JB fallback
-            // NOTE: THIS FALLBACK MECHANISM WILL BE REMOVED DUE TO ITS
-            // LARGE MEMORY REQUIREMENT
+        int32_t tunneled;
+        if (msg->findInt32("feature-tunneled-playback", &tunneled) &&
+            tunneled != 0) {
+            ALOGI("Configuring TUNNELED video playback.");
 
-            // we will not do adaptive playback on software accessed
-            // surfaces as they never had to respond to changes in the
-            // crop window, and we don't trust that they will be able to.
-            int usageBits = 0;
-            bool canDoAdaptivePlayback;
-
-            sp<NativeWindowWrapper> windowWrapper(
-                    static_cast<NativeWindowWrapper *>(obj.get()));
-            sp<ANativeWindow> nativeWindow = windowWrapper->getNativeWindow();
-
-            if (nativeWindow->query(
-                    nativeWindow.get(),
-                    NATIVE_WINDOW_CONSUMER_USAGE_BITS,
-                    &usageBits) != OK) {
-                canDoAdaptivePlayback = false;
-            } else {
-                canDoAdaptivePlayback =
-                    (usageBits &
-                            (GRALLOC_USAGE_SW_READ_MASK |
-                             GRALLOC_USAGE_SW_WRITE_MASK)) == 0;
+            int64_t audioHwSync = 0;
+            if (!msg->findInt64("audio-hw-sync", &audioHwSync)) {
+                ALOGW("No Audio HW Sync provided for video tunnel");
+            }
+            err = configureTunneledVideoPlayback(audioHwSync, nativeWindow);
+            if (err != OK) {
+                ALOGE("configureTunneledVideoPlayback(%" PRId64 ",%p) failed!",
+                        audioHwSync, nativeWindow.get());
+                return err;
             }
 
-            int32_t maxWidth = 0, maxHeight = 0;
-            if (canDoAdaptivePlayback &&
-                msg->findInt32("max-width", &maxWidth) &&
-                msg->findInt32("max-height", &maxHeight)) {
-                ALOGV("[%s] prepareForAdaptivePlayback(%dx%d)",
-                      mComponentName.c_str(), maxWidth, maxHeight);
-
-                err = mOMX->prepareForAdaptivePlayback(
-                        mNode, kPortIndexOutput, OMX_TRUE, maxWidth, maxHeight);
-                ALOGW_IF(err != OK,
-                        "[%s] prepareForAdaptivePlayback failed w/ err %d",
+            inputFormat->setInt32("adaptive-playback", true);
+        } else {
+            // Always try to enable dynamic output buffers on native surface
+            err = mOMX->storeMetaDataInBuffers(
+                    mNode, kPortIndexOutput, OMX_TRUE);
+            if (err != OK) {
+                ALOGE("[%s] storeMetaDataInBuffers failed w/ err %d",
                         mComponentName.c_str(), err);
 
-                if (err == OK) {
-                    inputFormat->setInt32("max-width", maxWidth);
-                    inputFormat->setInt32("max-height", maxHeight);
-                    inputFormat->setInt32("adaptive-playback", true);
-                }
-            }
-            // allow failure
-            err = OK;
-        } else {
-            ALOGV("[%s] storeMetaDataInBuffers succeeded", mComponentName.c_str());
-            mStoreMetaDataInOutputBuffers = true;
-            inputFormat->setInt32("adaptive-playback", true);
-        }
+                // if adaptive playback has been requested, try JB fallback
+                // NOTE: THIS FALLBACK MECHANISM WILL BE REMOVED DUE TO ITS
+                // LARGE MEMORY REQUIREMENT
 
-        int32_t push;
-        if (msg->findInt32("push-blank-buffers-on-shutdown", &push)
-                && push != 0) {
-            mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;
+                // we will not do adaptive playback on software accessed
+                // surfaces as they never had to respond to changes in the
+                // crop window, and we don't trust that they will be able to.
+                int usageBits = 0;
+                bool canDoAdaptivePlayback;
+
+                if (nativeWindow->query(
+                        nativeWindow.get(),
+                        NATIVE_WINDOW_CONSUMER_USAGE_BITS,
+                        &usageBits) != OK) {
+                    canDoAdaptivePlayback = false;
+                } else {
+                    canDoAdaptivePlayback =
+                        (usageBits &
+                                (GRALLOC_USAGE_SW_READ_MASK |
+                                 GRALLOC_USAGE_SW_WRITE_MASK)) == 0;
+                }
+
+                int32_t maxWidth = 0, maxHeight = 0;
+                if (canDoAdaptivePlayback &&
+                        msg->findInt32("max-width", &maxWidth) &&
+                        msg->findInt32("max-height", &maxHeight)) {
+                    ALOGV("[%s] prepareForAdaptivePlayback(%dx%d)",
+                            mComponentName.c_str(), maxWidth, maxHeight);
+
+                    err = mOMX->prepareForAdaptivePlayback(
+                            mNode, kPortIndexOutput, OMX_TRUE, maxWidth,
+                            maxHeight);
+                    ALOGW_IF(err != OK,
+                            "[%s] prepareForAdaptivePlayback failed w/ err %d",
+                            mComponentName.c_str(), err);
+
+                    if (err == OK) {
+                        inputFormat->setInt32("max-width", maxWidth);
+                        inputFormat->setInt32("max-height", maxHeight);
+                        inputFormat->setInt32("adaptive-playback", true);
+                    }
+                }
+                // allow failure
+                err = OK;
+            } else {
+                ALOGV("[%s] storeMetaDataInBuffers succeeded",
+                        mComponentName.c_str());
+                mStoreMetaDataInOutputBuffers = true;
+                inputFormat->setInt32("adaptive-playback", true);
+            }
+
+            int32_t push;
+            if (msg->findInt32("push-blank-buffers-on-shutdown", &push)
+                    && push != 0) {
+                mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;
+            }
         }
 
         int32_t rotationDegrees;
@@ -1290,6 +1354,8 @@
         } else {
             int32_t isADTS, aacProfile;
             int32_t sbrMode;
+            int32_t maxOutputChannelCount;
+            drcParams_t drc;
             if (!msg->findInt32("is-adts", &isADTS)) {
                 isADTS = 0;
             }
@@ -1300,9 +1366,33 @@
                 sbrMode = -1;
             }
 
+            if (!msg->findInt32("aac-max-output-channel_count", &maxOutputChannelCount)) {
+                maxOutputChannelCount = -1;
+            }
+            if (!msg->findInt32("aac-encoded-target-level", &drc.encodedTargetLevel)) {
+                // value is unknown
+                drc.encodedTargetLevel = -1;
+            }
+            if (!msg->findInt32("aac-drc-cut-level", &drc.drcCut)) {
+                // value is unknown
+                drc.drcCut = -1;
+            }
+            if (!msg->findInt32("aac-drc-boost-level", &drc.drcBoost)) {
+                // value is unknown
+                drc.drcBoost = -1;
+            }
+            if (!msg->findInt32("aac-drc-heavy-compression", &drc.heavyCompression)) {
+                // value is unknown
+                drc.heavyCompression = -1;
+            }
+            if (!msg->findInt32("aac-target-ref-level", &drc.targetRefLevel)) {
+                // value is unknown
+                drc.targetRefLevel = -1;
+            }
+
             err = setupAACCodec(
                     encoder, numChannels, sampleRate, bitRate, aacProfile,
-                    isADTS != 0, sbrMode);
+                    isADTS != 0, sbrMode, maxOutputChannelCount, drc);
         }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
         err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
@@ -1464,7 +1554,8 @@
 
 status_t ACodec::setupAACCodec(
         bool encoder, int32_t numChannels, int32_t sampleRate,
-        int32_t bitRate, int32_t aacProfile, bool isADTS, int32_t sbrMode) {
+        int32_t bitRate, int32_t aacProfile, bool isADTS, int32_t sbrMode,
+        int32_t maxOutputChannelCount, const drcParams_t& drc) {
     if (encoder && isADTS) {
         return -EINVAL;
     }
@@ -1587,8 +1678,23 @@
             ? OMX_AUDIO_AACStreamFormatMP4ADTS
             : OMX_AUDIO_AACStreamFormatMP4FF;
 
-    return mOMX->setParameter(
-            mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+    OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE presentation;
+    presentation.nMaxOutputChannels = maxOutputChannelCount;
+    presentation.nDrcCut = drc.drcCut;
+    presentation.nDrcBoost = drc.drcBoost;
+    presentation.nHeavyCompression = drc.heavyCompression;
+    presentation.nTargetReferenceLevel = drc.targetRefLevel;
+    presentation.nEncodedTargetLevel = drc.encodedTargetLevel;
+
+    status_t res = mOMX->setParameter(mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+    if (res == OK) {
+        // optional parameters, will not cause configuration failure
+        mOMX->setParameter(mNode, (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAacPresentation,
+                &presentation, sizeof(presentation));
+    } else {
+        ALOGW("did not set AudioAndroidAacPresentation due to error %d when setting AudioAac", res);
+    }
+    return res;
 }
 
 status_t ACodec::setupAC3Codec(
@@ -1785,6 +1891,27 @@
             mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
 }
 
+status_t ACodec::configureTunneledVideoPlayback(
+        int64_t audioHwSync, const sp<ANativeWindow> &nativeWindow) {
+    native_handle_t* sidebandHandle;
+
+    status_t err = mOMX->configureVideoTunnelMode(
+            mNode, kPortIndexOutput, OMX_TRUE, audioHwSync, &sidebandHandle);
+    if (err != OK) {
+        ALOGE("configureVideoTunnelMode failed! (err %d).", err);
+        return err;
+    }
+
+    err = native_window_set_sideband_stream(nativeWindow.get(), sidebandHandle);
+    if (err != OK) {
+        ALOGE("native_window_set_sideband_stream(%p) failed! (err %d).",
+                sidebandHandle, err);
+        return err;
+    }
+
+    return OK;
+}
+
 status_t ACodec::setVideoPortFormatType(
         OMX_U32 portIndex,
         OMX_VIDEO_CODINGTYPE compressionFormat,
@@ -3287,8 +3414,18 @@
 void ACodec::signalError(OMX_ERRORTYPE error, status_t internalError) {
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", CodecBase::kWhatError);
-    notify->setInt32("omx-error", error);
+    ALOGE("signalError(omxError %#x, internalError %d)", error, internalError);
+
+    if (internalError == UNKNOWN_ERROR) { // find better error code
+        const status_t omxStatus = statusFromOMXError(error);
+        if (omxStatus != 0) {
+            internalError = omxStatus;
+        } else {
+            ALOGW("Invalid OMX error %#x", error);
+        }
+    }
     notify->setInt32("err", internalError);
+    notify->setInt32("actionCode", ACTION_CODE_FATAL); // could translate from OMX error.
     notify->post();
 }
 
@@ -3512,6 +3649,7 @@
         case ACodec::kWhatCreateInputSurface:
         case ACodec::kWhatSignalEndOfInputStream:
         {
+            // This may result in an app illegal state exception.
             ALOGE("Message 0x%x was not handled", msg->what());
             mCodec->signalError(OMX_ErrorUndefined, INVALID_OPERATION);
             return true;
@@ -3519,6 +3657,7 @@
 
         case ACodec::kWhatOMXDied:
         {
+            // This will result in kFlagSawMediaServerDie handling in MediaCodec.
             ALOGE("OMX/mediaserver died, signalling error!");
             mCodec->signalError(OMX_ErrorResourcesLost, DEAD_OBJECT);
             break;
@@ -3617,7 +3756,13 @@
 
     ALOGE("[%s] ERROR(0x%08lx)", mCodec->mComponentName.c_str(), data1);
 
-    mCodec->signalError((OMX_ERRORTYPE)data1);
+    // verify OMX component sends back an error we expect.
+    OMX_ERRORTYPE omxError = (OMX_ERRORTYPE)data1;
+    if (!isOMXError(omxError)) {
+        ALOGW("Invalid OMX error %#x", omxError);
+        omxError = OMX_ErrorUndefined;
+    }
+    mCodec->signalError(omxError);
 
     return true;
 }
@@ -4060,7 +4205,7 @@
                     info->mGraphicBuffer.get(), -1)) == OK) {
             info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
         } else {
-            mCodec->signalError(OMX_ErrorUndefined, err);
+            mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
             info->mStatus = BufferInfo::OWNED_BY_US;
         }
     } else {
@@ -4432,7 +4577,7 @@
         ALOGE("[%s] configureCodec returning error %d",
               mCodec->mComponentName.c_str(), err);
 
-        mCodec->signalError(OMX_ErrorUndefined, err);
+        mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
         return false;
     }
 
@@ -4579,7 +4724,7 @@
              "(error 0x%08x)",
              err);
 
-        mCodec->signalError(OMX_ErrorUndefined, err);
+        mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
 
         mCodec->changeState(mCodec->mLoadedState);
     }
@@ -5107,7 +5252,7 @@
                          "port reconfiguration (error 0x%08x)",
                          err);
 
-                    mCodec->signalError(OMX_ErrorUndefined, err);
+                    mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
 
                     // This is technically not correct, but appears to be
                     // the only way to free the component instance.
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index a67a933..42691b9 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -113,24 +113,26 @@
 }
 // static
 sp<MediaCodec> MediaCodec::CreateByType(
-        const sp<ALooper> &looper, const char *mime, bool encoder) {
+        const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err) {
     sp<MediaCodec> codec = new MediaCodec(looper);
-    if (codec->init(mime, true /* nameIsType */, encoder) != OK) {
-        return NULL;
-    }
 
-    return codec;
+    const status_t ret = codec->init(mime, true /* nameIsType */, encoder);
+    if (err != NULL) {
+        *err = ret;
+    }
+    return ret == OK ? codec : NULL; // NULL deallocates codec.
 }
 
 // static
 sp<MediaCodec> MediaCodec::CreateByComponentName(
-        const sp<ALooper> &looper, const char *name) {
+        const sp<ALooper> &looper, const char *name, status_t *err) {
     sp<MediaCodec> codec = new MediaCodec(looper);
-    if (codec->init(name, false /* nameIsType */, false /* encoder */) != OK) {
-        return NULL;
-    }
 
-    return codec;
+    const status_t ret = codec->init(name, false /* nameIsType */, false /* encoder */);
+    if (err != NULL) {
+        *err = ret;
+    }
+    return ret == OK ? codec : NULL; // NULL deallocates codec.
 }
 
 MediaCodec::MediaCodec(const sp<ALooper> &looper)
@@ -139,6 +141,7 @@
       mCodec(NULL),
       mReplyID(0),
       mFlags(0),
+      mStickyError(OK),
       mSoftRenderer(NULL),
       mBatteryStatNotified(false),
       mIsVideo(false),
@@ -330,6 +333,7 @@
     mLooper->unregisterHandler(id());
 
     mFlags = 0;    // clear all flags
+    mStickyError = OK;
 
     // reset state not reset by setState(UNINITIALIZED)
     mReplyID = 0;
@@ -620,10 +624,12 @@
 
 bool MediaCodec::handleDequeueInputBuffer(uint32_t replyID, bool newRequest) {
     if (!isExecuting() || (mFlags & kFlagIsAsync)
-            || (mFlags & kFlagStickyError)
             || (newRequest && (mFlags & kFlagDequeueInputPending))) {
         PostReplyWithError(replyID, INVALID_OPERATION);
         return true;
+    } else if (mFlags & kFlagStickyError) {
+        PostReplyWithError(replyID, getStickyError());
+        return true;
     }
 
     ssize_t index = dequeuePortBuffer(kPortIndexInput);
@@ -644,9 +650,10 @@
     sp<AMessage> response = new AMessage;
 
     if (!isExecuting() || (mFlags & kFlagIsAsync)
-            || (mFlags & kFlagStickyError)
             || (newRequest && (mFlags & kFlagDequeueOutputPending))) {
         response->setInt32("err", INVALID_OPERATION);
+    } else if (mFlags & kFlagStickyError) {
+        response->setInt32("err", getStickyError());
     } else if (mFlags & kFlagOutputBuffersChanged) {
         response->setInt32("err", INFO_OUTPUT_BUFFERS_CHANGED);
         mFlags &= ~kFlagOutputBuffersChanged;
@@ -705,16 +712,12 @@
             switch (what) {
                 case CodecBase::kWhatError:
                 {
-                    int32_t omxError, internalError;
-                    CHECK(msg->findInt32("omx-error", &omxError));
-                    CHECK(msg->findInt32("err", &internalError));
+                    int32_t err, actionCode;
+                    CHECK(msg->findInt32("err", &err));
+                    CHECK(msg->findInt32("actionCode", &actionCode));
 
-                    ALOGE("Codec reported an error. "
-                          "(omx error 0x%08x, internalError %d)",
-                          omxError, internalError);
-
-                    if (omxError == OMX_ErrorResourcesLost
-                            && internalError == DEAD_OBJECT) {
+                    ALOGE("Codec reported err %#x, actionCode %d", err, actionCode);
+                    if (err == DEAD_OBJECT) {
                         mFlags |= kFlagSawMediaServerDie;
                     }
 
@@ -774,15 +777,24 @@
                         {
                             sendErrorReponse = false;
 
-                            mFlags |= kFlagStickyError;
+                            setStickyError(err);
                             postActivityNotificationIfPossible();
 
                             cancelPendingDequeueOperations();
 
                             if (mFlags & kFlagIsAsync) {
-                                onError(omxError, 0);
+                                onError(err, actionCode);
                             }
-                            setState(UNINITIALIZED);
+                            switch (actionCode) {
+                            case ACTION_CODE_TRANSIENT:
+                                break;
+                            case ACTION_CODE_RECOVERABLE:
+                                setState(INITIALIZED);
+                                break;
+                            default:
+                                setState(UNINITIALIZED);
+                                break;
+                            }
                             break;
                         }
 
@@ -790,19 +802,32 @@
                         {
                             sendErrorReponse = false;
 
-                            mFlags |= kFlagStickyError;
+                            setStickyError(err);
                             postActivityNotificationIfPossible();
 
-                            if (mFlags & kFlagIsAsync) {
-                                onError(omxError, 0);
+                            // actionCode in an uninitialized state is always fatal.
+                            if (mState == UNINITIALIZED) {
+                                actionCode = ACTION_CODE_FATAL;
                             }
-                            setState(UNINITIALIZED);
+                            if (mFlags & kFlagIsAsync) {
+                                onError(err, actionCode);
+                            }
+                            switch (actionCode) {
+                            case ACTION_CODE_TRANSIENT:
+                                break;
+                            case ACTION_CODE_RECOVERABLE:
+                                setState(INITIALIZED);
+                                break;
+                            default:
+                                setState(UNINITIALIZED);
+                                break;
+                            }
                             break;
                         }
                     }
 
                     if (sendErrorReponse) {
-                        PostReplyWithError(mReplyID, UNKNOWN_ERROR);
+                        PostReplyWithError(mReplyID, err);
                     }
                     break;
                 }
@@ -1009,7 +1034,7 @@
                             ALOGE("queueCSDInputBuffer failed w/ error %d",
                                   err);
 
-                            mFlags |= kFlagStickyError;
+                            setStickyError(err);
                             postActivityNotificationIfPossible();
 
                             cancelPendingDequeueOperations();
@@ -1401,9 +1426,12 @@
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
-            if (!isExecuting() || (mFlags & kFlagStickyError)) {
+            if (!isExecuting()) {
                 PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
+            } else if (mFlags & kFlagStickyError) {
+                PostReplyWithError(replyID, getStickyError());
+                break;
             }
 
             status_t err = onQueueInputBuffer(msg);
@@ -1472,9 +1500,12 @@
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
-            if (!isExecuting() || (mFlags & kFlagStickyError)) {
+            if (!isExecuting()) {
                 PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
+            } else if (mFlags & kFlagStickyError) {
+                PostReplyWithError(replyID, getStickyError());
+                break;
             }
 
             status_t err = onReleaseOutputBuffer(msg);
@@ -1488,9 +1519,12 @@
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
-            if (!isExecuting() || (mFlags & kFlagStickyError)) {
+            if (!isExecuting()) {
                 PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
+            } else if (mFlags & kFlagStickyError) {
+                PostReplyWithError(replyID, getStickyError());
+                break;
             }
 
             mReplyID = replyID;
@@ -1503,10 +1537,12 @@
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
-            if (!isExecuting() || (mFlags & kFlagIsAsync)
-                    || (mFlags & kFlagStickyError)) {
+            if (!isExecuting() || (mFlags & kFlagIsAsync)) {
                 PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
+            } else if (mFlags & kFlagStickyError) {
+                PostReplyWithError(replyID, getStickyError());
+                break;
             }
 
             int32_t portIndex;
@@ -1535,9 +1571,12 @@
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
-            if (!isExecuting() || (mFlags & kFlagStickyError)) {
+            if (!isExecuting()) {
                 PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
+            } else if (mFlags & kFlagStickyError) {
+                PostReplyWithError(replyID, getStickyError());
+                break;
             }
 
             mReplyID = replyID;
@@ -1561,10 +1600,12 @@
             if ((mState != CONFIGURED && mState != STARTING &&
                  mState != STARTED && mState != FLUSHING &&
                  mState != FLUSHED)
-                    || (mFlags & kFlagStickyError)
                     || format == NULL) {
                 PostReplyWithError(replyID, INVALID_OPERATION);
                 break;
+            } else if (mFlags & kFlagStickyError) {
+                PostReplyWithError(replyID, getStickyError());
+                break;
             }
 
             sp<AMessage> response = new AMessage;
@@ -1687,6 +1728,7 @@
         mFlags &= ~kFlagIsEncoder;
         mFlags &= ~kFlagGatherCodecSpecificData;
         mFlags &= ~kFlagIsAsync;
+        mStickyError = OK;
 
         mActivityNotify.clear();
         mCallback.clear();
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index aca21cf..ca031aa 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -78,6 +78,10 @@
             node_id node, OMX_U32 port_index, OMX_BOOL enable,
             OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight);
 
+    virtual status_t configureVideoTunnelMode(
+            node_id node, OMX_U32 portIndex, OMX_BOOL tunneled,
+            OMX_U32 audioHwSync, native_handle_t **sidebandHandle);
+
     virtual status_t enableGraphicBuffers(
             node_id node, OMX_U32 port_index, OMX_BOOL enable);
 
@@ -291,6 +295,13 @@
             node, port_index, enable, maxFrameWidth, maxFrameHeight);
 }
 
+status_t MuxOMX::configureVideoTunnelMode(
+        node_id node, OMX_U32 portIndex, OMX_BOOL enable,
+        OMX_U32 audioHwSync, native_handle_t **sidebandHandle) {
+    return getOMX(node)->configureVideoTunnelMode(
+            node, portIndex, enable, audioHwSync, sidebandHandle);
+}
+
 status_t MuxOMX::enableGraphicBuffers(
         node_id node, OMX_U32 port_index, OMX_BOOL enable) {
     return getOMX(node)->enableGraphicBuffers(node, port_index, enable);
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 7124fd3..a4a651d 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -439,6 +439,10 @@
         maxBytesToRead = maxBytesAvailable;
     }
 
+    // read only integral amounts of audio unit frames.
+    const size_t inputUnitFrameSize = mNumChannels * mBitsPerSample / 8;
+    maxBytesToRead -= maxBytesToRead % inputUnitFrameSize;
+
     if (mWaveFormat == WAVE_FORMAT_MSGSM) {
         // Microsoft packs 2 frames into 65 bytes, rather than using separate 33-byte frames,
         // so read multiples of 65, and use smaller buffers to account for ~10:1 expansion ratio
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index ab30865..09c6e69 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -19,6 +19,8 @@
 #include <utils/Log.h>
 
 #include "SoftAAC2.h"
+#include <OMX_AudioExt.h>
+#include <OMX_IndexExt.h>
 
 #include <cutils/properties.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -119,6 +121,7 @@
 }
 
 status_t SoftAAC2::initDecoder() {
+    ALOGV("initDecoder()");
     status_t status = UNKNOWN_ERROR;
     mAACDecoder = aacDecoder_Open(TT_MP4_ADIF, /* num layers */ 1);
     if (mAACDecoder != NULL) {
@@ -275,7 +278,7 @@
 
 OMX_ERRORTYPE SoftAAC2::internalSetParameter(
         OMX_INDEXTYPE index, const OMX_PTR params) {
-    switch (index) {
+    switch ((int)index) {
         case OMX_IndexParamStandardComponentRole:
         {
             const OMX_PARAM_COMPONENTROLETYPE *roleParams =
@@ -311,6 +314,67 @@
             return OMX_ErrorNone;
         }
 
+        case OMX_IndexParamAudioAndroidAacPresentation:
+        {
+            const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *aacPresParams =
+                    (const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *)params;
+            // for the following parameters of the OMX_AUDIO_PARAM_AACPROFILETYPE structure,
+            // a value of -1 implies the parameter is not set by the application:
+            //   nMaxOutputChannels     uses default platform properties, see configureDownmix()
+            //   nDrcCut                uses default platform properties, see initDecoder()
+            //   nDrcBoost                idem
+            //   nHeavyCompression        idem
+            //   nTargetReferenceLevel    idem
+            //   nEncodedTargetLevel      idem
+            if (aacPresParams->nMaxOutputChannels >= 0) {
+                int max;
+                if (aacPresParams->nMaxOutputChannels >= 8) { max = 8; }
+                else if (aacPresParams->nMaxOutputChannels >= 6) { max = 6; }
+                else if (aacPresParams->nMaxOutputChannels >= 2) { max = 2; }
+                else {
+                    // -1 or 0: disable downmix,  1: mono
+                    max = aacPresParams->nMaxOutputChannels;
+                }
+                ALOGV("set nMaxOutputChannels=%d", max);
+                aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, max);
+            }
+            bool updateDrcWrapper = false;
+            if (aacPresParams->nDrcBoost >= 0) {
+                ALOGV("set nDrcBoost=%d", aacPresParams->nDrcBoost);
+                mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR,
+                        aacPresParams->nDrcBoost);
+                updateDrcWrapper = true;
+            }
+            if (aacPresParams->nDrcCut >= 0) {
+                ALOGV("set nDrcCut=%d", aacPresParams->nDrcCut);
+                mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, aacPresParams->nDrcCut);
+                updateDrcWrapper = true;
+            }
+            if (aacPresParams->nHeavyCompression >= 0) {
+                ALOGV("set nHeavyCompression=%d", aacPresParams->nHeavyCompression);
+                mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY,
+                        aacPresParams->nHeavyCompression);
+                updateDrcWrapper = true;
+            }
+            if (aacPresParams->nTargetReferenceLevel >= 0) {
+                ALOGV("set nTargetReferenceLevel=%d", aacPresParams->nTargetReferenceLevel);
+                mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET,
+                        aacPresParams->nTargetReferenceLevel);
+                updateDrcWrapper = true;
+            }
+            if (aacPresParams->nEncodedTargetLevel >= 0) {
+                ALOGV("set nEncodedTargetLevel=%d", aacPresParams->nEncodedTargetLevel);
+                mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET,
+                        aacPresParams->nEncodedTargetLevel);
+                updateDrcWrapper = true;
+            }
+            if (updateDrcWrapper) {
+                mDrcWrap.update();
+            }
+
+            return OMX_ErrorNone;
+        }
+
         case OMX_IndexParamAudioPcm:
         {
             const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 67dfcd2..0c5527a 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -112,6 +112,17 @@
                 bufHeight,
                 halFormat));
 
+    // NOTE: native window uses extended right-bottom coordinate
+    android_native_rect_t crop;
+    crop.left = mCropLeft;
+    crop.top = mCropTop;
+    crop.right = mCropRight + 1;
+    crop.bottom = mCropBottom + 1;
+    ALOGV("setting crop: [%d, %d, %d, %d] for size [%zu, %zu]",
+          crop.left, crop.top, crop.right, crop.bottom, bufWidth, bufHeight);
+
+    CHECK_EQ(0, native_window_set_crop(mNativeWindow.get(), &crop));
+
     uint32_t transform;
     switch (rotationDegrees) {
         case 0: transform = 0; break;
diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
index bf474e1..9835ca3 100644
--- a/media/libstagefright/foundation/AString.cpp
+++ b/media/libstagefright/foundation/AString.cpp
@@ -358,7 +358,7 @@
 }
 
 status_t AString::writeToParcel(Parcel *parcel) const {
-    CHECK_LE(mSize, (size_t)INT32_MAX);
+    CHECK_LE(mSize, static_cast<size_t>(INT32_MAX));
     status_t err = parcel->writeInt32(mSize);
     if (err == OK) {
         err = parcel->write(mData, mSize);
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index cd51bbf..e8c4970 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -75,6 +75,10 @@
             node_id node, OMX_U32 portIndex, OMX_BOOL enable,
             OMX_U32 max_frame_width, OMX_U32 max_frame_height);
 
+    virtual status_t configureVideoTunnelMode(
+            node_id node, OMX_U32 portIndex, OMX_BOOL tunneled,
+            OMX_U32 audioHwSync, native_handle_t **sidebandHandle);
+
     virtual status_t useBuffer(
             node_id node, OMX_U32 port_index, const sp<IMemory> &params,
             buffer_id *buffer);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index 3967dc6..dc6d410 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -62,6 +62,10 @@
             OMX_U32 portIndex, OMX_BOOL enable,
             OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight);
 
+    status_t configureVideoTunnelMode(
+            OMX_U32 portIndex, OMX_BOOL tunneled,
+            OMX_U32 audioHwSync, native_handle_t **sidebandHandle);
+
     status_t useBuffer(
             OMX_U32 portIndex, const sp<IMemory> &params,
             OMX::buffer_id *buffer);
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index cc4770a..41407e4 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -342,6 +342,13 @@
             portIndex, enable, maxFrameWidth, maxFrameHeight);
 }
 
+status_t OMX::configureVideoTunnelMode(
+        node_id node, OMX_U32 portIndex, OMX_BOOL tunneled,
+        OMX_U32 audioHwSync, native_handle_t **sidebandHandle) {
+    return findInstance(node)->configureVideoTunnelMode(
+            portIndex, tunneled, audioHwSync, sidebandHandle);
+}
+
 status_t OMX::useBuffer(
         node_id node, OMX_U32 port_index, const sp<IMemory> &params,
         buffer_id *buffer) {
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index d6ab109..efb27f5 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -460,6 +460,49 @@
     return err;
 }
 
+status_t OMXNodeInstance::configureVideoTunnelMode(
+        OMX_U32 portIndex, OMX_BOOL tunneled, OMX_U32 audioHwSync,
+        native_handle_t **sidebandHandle) {
+    Mutex::Autolock autolock(mLock);
+
+    OMX_INDEXTYPE index;
+    OMX_STRING name = const_cast<OMX_STRING>(
+            "OMX.google.android.index.configureVideoTunnelMode");
+
+    OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
+    if (err != OMX_ErrorNone) {
+        ALOGE("configureVideoTunnelMode extension is missing!");
+        return StatusFromOMXError(err);
+    }
+
+    ConfigureVideoTunnelModeParams tunnelParams;
+    tunnelParams.nSize = sizeof(tunnelParams);
+    tunnelParams.nVersion.s.nVersionMajor = 1;
+    tunnelParams.nVersion.s.nVersionMinor = 0;
+    tunnelParams.nVersion.s.nRevision = 0;
+    tunnelParams.nVersion.s.nStep = 0;
+
+    tunnelParams.nPortIndex = portIndex;
+    tunnelParams.bTunneled = tunneled;
+    tunnelParams.nAudioHwSync = audioHwSync;
+    err = OMX_SetParameter(mHandle, index, &tunnelParams);
+    if (err != OMX_ErrorNone) {
+        ALOGE("configureVideoTunnelMode failed! (err %d).", err);
+        return UNKNOWN_ERROR;
+    }
+
+    err = OMX_GetParameter(mHandle, index, &tunnelParams);
+    if (err != OMX_ErrorNone) {
+        ALOGE("GetVideoTunnelWindow failed! (err %d).", err);
+        return UNKNOWN_ERROR;
+    }
+    if (sidebandHandle) {
+        *sidebandHandle = (native_handle_t*)tunnelParams.pSidebandWindow;
+    }
+
+    return err;
+}
+
 status_t OMXNodeInstance::useBuffer(
         OMX_U32 portIndex, const sp<IMemory> &params,
         OMX::buffer_id *buffer) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 4e9d49b..753314f 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -343,7 +343,8 @@
             uint32_t channelCount = FCC_2; // stereo is default
             if (kEnableExtendedChannels) {
                 channelCount = audio_channel_count_from_out_mask(channelMask);
-                if (channelCount > AudioMixer::MAX_NUM_CHANNELS) {
+                if (channelCount < FCC_2 // mono is not supported at this time
+                        || channelCount > AudioMixer::MAX_NUM_CHANNELS) {
                     return false;
                 }
             }
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 49422a9..2d0a25f 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -151,12 +151,15 @@
     if (handle == NULL || patch == NULL) {
         return BAD_VALUE;
     }
-    // limit number of sources to 1 for now or 2 sources for special cross hw module case.
-    // only the audio policy manager can request a patch creation with 2 sources.
-    if (patch->num_sources == 0 || patch->num_sources > 2 ||
+    if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX ||
             patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
         return BAD_VALUE;
     }
+    // limit number of sources to 1 for now or 2 sources for special cross hw module case.
+    // only the audio policy manager can request a patch creation with 2 sources.
+    if (patch->num_sources > 2) {
+        return INVALID_OPERATION;
+    }
 
     if (*handle != AUDIO_PATCH_HANDLE_NONE) {
         for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) {
@@ -173,20 +176,22 @@
 
     switch (patch->sources[0].type) {
         case AUDIO_PORT_TYPE_DEVICE: {
-            // limit number of sinks to 1 for now
-            if (patch->num_sinks > 1) {
-                status = BAD_VALUE;
-                goto exit;
-            }
-            audio_module_handle_t src_module = patch->sources[0].ext.device.hw_module;
-            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(src_module);
+            audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
+            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
             if (index < 0) {
-                ALOGW("createAudioPatch() bad src hw module %d", src_module);
+                ALOGW("createAudioPatch() bad src hw module %d", srcModule);
                 status = BAD_VALUE;
                 goto exit;
             }
             AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
             for (unsigned int i = 0; i < patch->num_sinks; i++) {
+                // support only one sink if connection to a mix or across HW modules
+                if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX ||
+                        patch->sinks[i].ext.mix.hw_module != srcModule) &&
+                        patch->num_sinks > 1) {
+                    status = INVALID_OPERATION;
+                    goto exit;
+                }
                 // 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");
@@ -194,7 +199,7 @@
                     goto exit;
                 }
                 // limit to connections between devices and input streams for HAL before 3.0
-                if (patch->sinks[i].ext.mix.hw_module == src_module &&
+                if (patch->sinks[i].ext.mix.hw_module == srcModule &&
                         (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",
@@ -204,7 +209,7 @@
                 }
             }
 
-            if (patch->sinks[0].ext.device.hw_module != src_module) {
+            if (patch->sinks[0].ext.device.hw_module != srcModule) {
                 // limit to device to device connection if not on same hw module
                 if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
                     ALOGW("createAudioPatch() invalid sink type for cross hw module");
@@ -258,7 +263,7 @@
                 config.channel_mask = inChannelMask;
                 config.format = newPatch->mPlaybackThread->format();
                 audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
-                newPatch->mRecordThread = audioflinger->openInput_l(src_module,
+                newPatch->mRecordThread = audioflinger->openInput_l(srcModule,
                                                                     &input,
                                                                     &config,
                                                                     device,
@@ -279,10 +284,10 @@
                 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);
+                                                                  patch->sinks[0].ext.mix.handle);
                         if (thread == 0) {
                             ALOGW("createAudioPatch() bad capture I/O handle %d",
-                                                                      patch->sinks[0].ext.mix.handle);
+                                                                  patch->sinks[0].ext.mix.handle);
                             status = BAD_VALUE;
                             goto exit;
                         }
@@ -298,10 +303,10 @@
                     }
                 } else {
                     sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
-                                                                        patch->sinks[0].ext.mix.handle);
+                                                                    patch->sinks[0].ext.mix.handle);
                     if (thread == 0) {
                         ALOGW("createAudioPatch() bad capture I/O handle %d",
-                                                                      patch->sinks[0].ext.mix.handle);
+                                                                    patch->sinks[0].ext.mix.handle);
                         status = BAD_VALUE;
                         goto exit;
                     }
@@ -326,10 +331,10 @@
             }
         } 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);
+            audio_module_handle_t srcModule =  patch->sources[0].ext.mix.hw_module;
+            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
             if (index < 0) {
-                ALOGW("createAudioPatch() bad src hw module %d", src_module);
+                ALOGW("createAudioPatch() bad src hw module %d", srcModule);
                 status = BAD_VALUE;
                 goto exit;
             }
@@ -342,7 +347,7 @@
                     goto exit;
                 }
                 // limit to connections between sinks and sources on same HW module
-                if (patch->sinks[i].ext.device.hw_module != src_module) {
+                if (patch->sinks[i].ext.device.hw_module != srcModule) {
                     status = BAD_VALUE;
                     goto exit;
                 }
@@ -365,6 +370,7 @@
                 }
                 char *address;
                 if (strcmp(patch->sinks[0].ext.device.address, "") != 0) {
+                    //FIXME: we only support address on first sink with HAL version < 3.0
                     address = audio_device_address_to_parameter(
                                                                 patch->sinks[0].ext.device.type,
                                                                 patch->sinks[0].ext.device.address);
@@ -562,16 +568,16 @@
 
     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);
+            audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
+            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
             if (index < 0) {
-                ALOGW("releaseAudioPatch() bad src hw module %d", src_module);
+                ALOGW("releaseAudioPatch() bad src hw module %d", srcModule);
                 status = BAD_VALUE;
                 break;
             }
 
             if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
-                    patch->sinks[0].ext.device.hw_module != src_module) {
+                    patch->sinks[0].ext.device.hw_module != srcModule) {
                 clearPatchConnections(removedPatch);
                 break;
             }
@@ -609,10 +615,10 @@
             }
         } 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);
+            audio_module_handle_t srcModule =  patch->sources[0].ext.mix.hw_module;
+            ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
             if (index < 0) {
-                ALOGW("releaseAudioPatch() bad src hw module %d", src_module);
+                ALOGW("releaseAudioPatch() bad src hw module %d", srcModule);
                 status = BAD_VALUE;
                 break;
             }
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 2f65370..7d583bb5 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -910,11 +910,11 @@
         goto Exit;
     }
 
-    // Reject any effect on multichannel sinks.
+    // Reject any effect on mixer or duplicating multichannel sinks.
     // TODO: fix both format and multichannel issues with effects.
-    if (mChannelCount != FCC_2) {
-        ALOGW("createEffect_l() Cannot add effect %s for multichannel(%d) thread",
-                desc->name, mChannelCount);
+    if ((mType == MIXER || mType == DUPLICATING) && mChannelCount != FCC_2) {
+        ALOGW("createEffect_l() Cannot add effect %s for multichannel(%d) %s threads",
+                desc->name, mChannelCount, mType == MIXER ? "MIXER" : "DUPLICATING");
         lStatus = BAD_VALUE;
         goto Exit;
     }
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index f95b839..850fe86 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -2078,22 +2078,37 @@
     }
     ALOGV("createAudioPatch() num sources %d num sinks %d", patch->num_sources, patch->num_sinks);
 
-    if (patch->num_sources > 1 || patch->num_sinks > 1) {
+    if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX ||
+            patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
+        return BAD_VALUE;
+    }
+    // only one source per audio patch supported for now
+    if (patch->num_sources > 1) {
         return INVALID_OPERATION;
     }
-    if (patch->sources[0].role != AUDIO_PORT_ROLE_SOURCE ||
-            patch->sinks[0].role != AUDIO_PORT_ROLE_SINK) {
+
+    if (patch->sources[0].role != AUDIO_PORT_ROLE_SOURCE) {
         return INVALID_OPERATION;
     }
+    for (size_t i = 0; i < patch->num_sinks; i++) {
+        if (patch->sinks[i].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 LOG_NDEBUG == 0
+    for (size_t i = 0; i < patch->num_sinks; i++) {
+        ALOGV("createAudioPatch sink %d: id %d role %d type %d", i, patch->sinks[i].id,
+                                                             patch->sinks[i].role,
+                                                             patch->sinks[i].type);
+    }
+#endif
 
     if (index >= 0) {
         patchDesc = mAudioPatches.valueAt(index);
@@ -2107,12 +2122,6 @@
     }
 
     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
         sp<AudioOutputDescriptor> outputDesc = getOutputFromId(patch->sources[0].id);
         if (outputDesc == NULL) {
             ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
@@ -2127,30 +2136,41 @@
                 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;
-        }
+        DeviceVector devices;
+        for (size_t i = 0; i < patch->num_sinks; i++) {
+            // Only support mix to devices connection
+            // TODO add support for mix to mix connection
+            if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
+                ALOGV("createAudioPatch() source mix but sink is not a device");
+                return INVALID_OPERATION;
+            }
+            sp<DeviceDescriptor> devDesc =
+                    mAvailableOutputDevices.getDeviceFromId(patch->sinks[i].id);
+            if (devDesc == 0) {
+                ALOGV("createAudioPatch() out device not found for id %d", patch->sinks[i].id);
+                return BAD_VALUE;
+            }
 
-        if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType,
-                                                       patch->sources[0].sample_rate,
-                                                     NULL,  // updatedSamplingRate
-                                                     patch->sources[0].format,
-                                                     patch->sources[0].channel_mask,
-                                                     AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) {
-            ALOGV("createAudioPatch() profile not supported");
+            if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType,
+                                                           patch->sources[0].sample_rate,
+                                                         NULL,  // updatedSamplingRate
+                                                         patch->sources[0].format,
+                                                         patch->sources[0].channel_mask,
+                                                         AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) {
+                ALOGV("createAudioPatch() profile not supported for device %08x",
+                      devDesc->mDeviceType);
+                return INVALID_OPERATION;
+            }
+            devices.add(devDesc);
+        }
+        if (devices.size() == 0) {
             return INVALID_OPERATION;
         }
+
         // TODO: reconfigure output format and channels here
         ALOGV("createAudioPatch() setting device %08x on output %d",
-                                              devDesc->mDeviceType, outputDesc->mIoHandle);
-        setOutputDevice(outputDesc->mIoHandle,
-                        devDesc->mDeviceType,
-                       true,
-                       0,
-                       handle);
+              devices.types(), outputDesc->mIoHandle);
+        setOutputDevice(outputDesc->mIoHandle, devices.types(), true, 0, handle);
         index = mAudioPatches.indexOfKey(*handle);
         if (index >= 0) {
             if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
@@ -2166,6 +2186,10 @@
     } 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
+            // only one sink supported when connecting an input device to a mix
+            if (patch->num_sinks > 1) {
+                return INVALID_OPERATION;
+            }
             sp<AudioInputDescriptor> inputDesc = getInputFromId(patch->sinks[0].id);
             if (inputDesc == NULL) {
                 return BAD_VALUE;
@@ -2195,10 +2219,7 @@
             // TODO: reconfigure output format and channels here
             ALOGV("createAudioPatch() setting device %08x on output %d",
                                                   devDesc->mDeviceType, inputDesc->mIoHandle);
-            setInputDevice(inputDesc->mIoHandle,
-                           devDesc->mDeviceType,
-                           true,
-                           handle);
+            setInputDevice(inputDesc->mIoHandle, devDesc->mDeviceType, true, handle);
             index = mAudioPatches.indexOfKey(*handle);
             if (index >= 0) {
                 if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
@@ -2214,38 +2235,53 @@
         } 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) {
+                if (patchDesc->mPatch.sources[0].id != patch->sources[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]);
+            if (srcDeviceDesc == 0) {
+                return BAD_VALUE;
+            }
 
-            if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
-                SortedVector<audio_io_handle_t> outputs =
-                                        getOutputsForDevice(sinkDeviceDesc->mDeviceType, mOutputs);
-                // if the sink device is reachable via an opened output stream, request to go via
-                // this output stream by adding a second source to the patch description
-                audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE);
-                if (output != AUDIO_IO_HANDLE_NONE) {
-                    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
-                    if (outputDesc->isDuplicated()) {
+            for (size_t i = 0; i < patch->num_sinks; i++) {
+                if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
+                    ALOGV("createAudioPatch() source device but one sink is not a device");
+                    return INVALID_OPERATION;
+                }
+
+                sp<DeviceDescriptor> sinkDeviceDesc =
+                        mAvailableOutputDevices.getDeviceFromId(patch->sinks[i].id);
+                if (sinkDeviceDesc == 0) {
+                    return BAD_VALUE;
+                }
+                sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]);
+
+                if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
+                    // only one sink supported when connected devices across HW modules
+                    if (patch->num_sinks > 1) {
                         return INVALID_OPERATION;
                     }
-                    outputDesc->toAudioPortConfig(&newPatch.sources[1], &patch->sources[0]);
-                    newPatch.num_sources = 2;
+                    SortedVector<audio_io_handle_t> outputs =
+                                            getOutputsForDevice(sinkDeviceDesc->mDeviceType,
+                                                                mOutputs);
+                    // if the sink device is reachable via an opened output stream, request to go via
+                    // this output stream by adding a second source to the patch description
+                    audio_io_handle_t output = selectOutput(outputs, AUDIO_OUTPUT_FLAG_NONE);
+                    if (output != AUDIO_IO_HANDLE_NONE) {
+                        sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+                        if (outputDesc->isDuplicated()) {
+                            return INVALID_OPERATION;
+                        }
+                        outputDesc->toAudioPortConfig(&newPatch.sources[1], &patch->sources[0]);
+                        newPatch.num_sources = 2;
+                    }
                 }
             }
             // TODO: check from routing capabilities in config file and other conflicting patches