Merge "mediaplayer: add precise argument to seek function"
diff --git a/Android.bp b/Android.bp
index edd3d04..53f8bec 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,6 +3,7 @@
     from: "include/camera/ndk/",
     to: "camera",
     srcs: ["include/camera/ndk/**/*.h"],
+    license: "NOTICE",
 }
 
 ndk_headers {
@@ -10,6 +11,7 @@
     from: "include/ndk/",
     to: "media",
     srcs: ["include/ndk/**/*.h"],
+    license: "NOTICE",
 }
 
 subdirs = [
diff --git a/camera/Android.mk b/camera/Android.mk
index 1a3382f..c9c98e9 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -58,8 +58,6 @@
 	libutils \
 	liblog \
 	libbinder \
-	libhardware \
-	libui \
 	libgui \
 	libcamera_metadata \
 
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index 755ec8e..8308095 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -44,4 +44,5 @@
      * @param lastFrameNumber Frame number of the last frame of the streaming request.
      */
     oneway void onRepeatingRequestError(in long lastFrameNumber);
+    oneway void onRequestQueueEmpty();
 }
diff --git a/camera/cameraserver/Android.mk b/camera/cameraserver/Android.mk
index c0d75f3..888862a 100644
--- a/camera/cameraserver/Android.mk
+++ b/camera/cameraserver/Android.mk
@@ -25,7 +25,6 @@
 	libcutils \
 	libutils \
 	libbinder \
-	libcamera_client
 
 LOCAL_MODULE:= cameraserver
 LOCAL_32_BIT_ONLY := true
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 7d78e2b..229b159 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -1347,6 +1347,12 @@
 }
 
 binder::Status
+CameraDevice::ServiceCallback::onRequestQueueEmpty() {
+    // onRequestQueueEmpty not yet implemented in NDK
+    return binder::Status::ok();
+}
+
+binder::Status
 CameraDevice::ServiceCallback::onRepeatingRequestError(int64_t lastFrameNumber) {
     binder::Status ret = binder::Status::ok();
 
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 051462b..eb8028b 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -74,6 +74,7 @@
         binder::Status onResultReceived(const CameraMetadata& metadata,
                               const CaptureResultExtras& resultExtras) override;
         binder::Status onPrepared(int streamId) override;
+        binder::Status onRequestQueueEmpty() override;
         binder::Status onRepeatingRequestError(int64_t lastFrameNumber) override;
       private:
         const wp<CameraDevice> mDevice;
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 828a758..b91e0f3 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -151,6 +151,7 @@
         SENT_RESULT,
         UNINITIALIZED,
         REPEATING_REQUEST_ERROR,
+        REQUEST_QUEUE_EMPTY,
     };
 
 protected:
@@ -225,6 +226,14 @@
         return binder::Status::ok();
     }
 
+    virtual binder::Status onRequestQueueEmpty() {
+        Mutex::Autolock l(mLock);
+        mLastStatus = REQUEST_QUEUE_EMPTY;
+        mStatusesHit.push_back(mLastStatus);
+        mStatusCondition.broadcast();
+        return binder::Status::ok();
+    }
+
     // Test helper functions:
 
     bool hadError() const {
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 1a4bf08..8fe1dd4 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -230,7 +230,6 @@
                     mCodec->signalResume();
 
                     (new AMessage(kWhatSeek, this))->post(5000000ll);
-                } else if (what == CodecBase::kWhatOutputFormatChanged) {
                 } else if (what == CodecBase::kWhatShutdownCompleted) {
                     mDecodeLooper->unregisterHandler(mCodec->id());
 
diff --git a/drm/libmediadrm/Android.mk b/drm/libmediadrm/Android.mk
index c822433..270f291 100644
--- a/drm/libmediadrm/Android.mk
+++ b/drm/libmediadrm/Android.mk
@@ -14,13 +14,11 @@
 
 LOCAL_SHARED_LIBRARIES := \
 	libbinder \
-	libcrypto \
 	libcutils \
 	libdl \
 	liblog \
 	libmedia \
 	libstagefright \
-	libstagefright_foundation \
 	libutils
 
 LOCAL_CFLAGS += -Werror -Wno-error=deprecated-declarations -Wall
diff --git a/drm/libmediadrm/Drm.cpp b/drm/libmediadrm/Drm.cpp
index 9ab08db..07e9414 100644
--- a/drm/libmediadrm/Drm.cpp
+++ b/drm/libmediadrm/Drm.cpp
@@ -334,6 +334,7 @@
         return -EINVAL;
     }
 
+    setListener(NULL);
     delete mPlugin;
     mPlugin = NULL;
 
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index b2699fa..0533ba6 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -330,8 +330,8 @@
 
     static status_t startAudioSource(const struct audio_port_config *source,
                                       const audio_attributes_t *attributes,
-                                      audio_io_handle_t *handle);
-    static status_t stopAudioSource(audio_io_handle_t handle);
+                                      audio_patch_handle_t *handle);
+    static status_t stopAudioSource(audio_patch_handle_t handle);
 
     static status_t setMasterMono(bool mono);
     static status_t getMasterMono(bool *mono);
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index ef15a0c..5637dd5 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -164,8 +164,8 @@
 
     virtual status_t startAudioSource(const struct audio_port_config *source,
                                       const audio_attributes_t *attributes,
-                                      audio_io_handle_t *handle) = 0;
-    virtual status_t stopAudioSource(audio_io_handle_t handle) = 0;
+                                      audio_patch_handle_t *handle) = 0;
+    virtual status_t stopAudioSource(audio_patch_handle_t handle) = 0;
 
     virtual status_t setMasterMono(bool mono) = 0;
     virtual status_t getMasterMono(bool *mono) = 0;
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index b902cf5..839945c 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -19,7 +19,7 @@
 #define ANDROID_IOMX_H_
 
 #include <binder/IInterface.h>
-#include <ui/GraphicBuffer.h>
+#include <gui/IGraphicBufferProducer.h>
 #include <utils/List.h>
 #include <utils/String8.h>
 
@@ -39,6 +39,7 @@
 class IOMXNode;
 class IOMXObserver;
 class NativeHandle;
+class OMXBuffer;
 struct omx_message;
 
 class IOMX : public IInterface {
@@ -108,23 +109,6 @@
     virtual status_t getGraphicBufferUsage(
             OMX_U32 port_index, OMX_U32* usage) = 0;
 
-    // Use |params| as an OMX buffer, but limit the size of the OMX buffer to |allottedSize|.
-    virtual status_t useBuffer(
-            OMX_U32 port_index, const sp<IMemory> &params,
-            buffer_id *buffer, OMX_U32 allottedSize) = 0;
-
-    virtual status_t useGraphicBuffer(
-            OMX_U32 port_index,
-            const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) = 0;
-
-    virtual status_t updateGraphicBufferInMeta(
-            OMX_U32 port_index,
-            const sp<GraphicBuffer> &graphicBuffer, buffer_id buffer) = 0;
-
-    virtual status_t updateNativeHandleInMeta(
-            OMX_U32 port_index,
-            const sp<NativeHandle> &nativeHandle, buffer_id buffer) = 0;
-
     virtual status_t setInputSurface(
             const sp<IOMXBufferSource> &bufferSource) = 0;
 
@@ -137,34 +121,37 @@
             OMX_U32 port_index, size_t size, buffer_id *buffer,
             void **buffer_data, sp<NativeHandle> *native_handle) = 0;
 
+    // Instructs the component to use the buffer passed in via |omxBuf| on the
+    // specified port. Returns in |*buffer| the buffer id that the component
+    // assigns to this buffer. |omxBuf| must be one of:
+    // 1) OMXBuffer::sPreset for meta-mode,
+    // 2) type kBufferTypeANWBuffer for non-meta-graphic buffer mode,
+    // 3) type kBufferTypeSharedMem for bytebuffer mode.
+    virtual status_t useBuffer(
+            OMX_U32 port_index, const OMXBuffer &omxBuf, buffer_id *buffer) = 0;
+
+    // Frees the buffer on the specified port with buffer id |buffer|.
     virtual status_t freeBuffer(
             OMX_U32 port_index, buffer_id buffer) = 0;
 
-    // Calls OMX_FillBuffer on buffer, and passes |fenceFd| to component if it supports
-    // fences. Otherwise, it waits on |fenceFd| before calling OMX_FillBuffer.
-    // Takes ownership of |fenceFd| even if this call fails.
-    virtual status_t fillBuffer(buffer_id buffer, int fenceFd = -1) = 0;
+    // Calls OMX_FillBuffer on buffer. Passes |fenceFd| to component if it
+    // supports fences. Otherwise, it waits on |fenceFd| before calling
+    // OMX_FillBuffer. Takes ownership of |fenceFd| even if this call fails.
+    // If the port is in metadata mode, the buffer will be updated to point
+    // to the new buffer passed in via |omxBuf| before OMX_FillBuffer is called.
+    // Otherwise info in the |omxBuf| is not used.
+    virtual status_t fillBuffer(
+            buffer_id buffer, const OMXBuffer &omxBuf, int fenceFd = -1) = 0;
 
-    // Calls OMX_EmptyBuffer on buffer (after updating buffer header with |range_offset|,
-    // |range_length|, |flags| and |timestamp|). Passes |fenceFd| to component if it
-    // supports fences. Otherwise, it waits on |fenceFd| before calling OMX_EmptyBuffer.
-    // Takes ownership of |fenceFd| even if this call fails.
+    // Calls OMX_EmptyBuffer on buffer. Passes |fenceFd| to component if it
+    // supports fences. Otherwise, it waits on |fenceFd| before calling
+    // OMX_EmptyBuffer. Takes ownership of |fenceFd| even if this call fails.
+    // If the port is in metadata mode, the buffer will be updated to point
+    // to the new buffer passed in via |omxBuf| before OMX_EmptyBuffer is called.
     virtual status_t emptyBuffer(
-            buffer_id buffer,
-            OMX_U32 range_offset, OMX_U32 range_length,
+            buffer_id buffer, const OMXBuffer &omxBuf,
             OMX_U32 flags, OMX_TICKS timestamp, int fenceFd = -1) = 0;
 
-    // Calls OMX_EmptyBuffer on buffer (after updating buffer header with metadata of
-    // |graphicBuffer|, |flags| and |timestamp|). Passes |fenceFd| to component if it
-    // supports fences. Otherwise, it waits on |fenceFd| before calling OMX_EmptyBuffer.
-    // Takes ownership of |fenceFd| even if this call fails. If |origTimestamp| >= 0,
-    // timestamp on the filled buffer corresponding to this frame will be modified to
-    // |origTimestamp| after it's filled.
-    virtual status_t emptyGraphicBuffer(
-            buffer_id buffer,
-            const sp<GraphicBuffer> &graphicBuffer, OMX_U32 flags,
-            OMX_TICKS timestamp, int fenceFd) = 0;
-
     virtual status_t getExtensionIndex(
             const char *parameter_name,
             OMX_INDEXTYPE *index) = 0;
diff --git a/include/media/MediaCodecBuffer.h b/include/media/MediaCodecBuffer.h
index 2df81dd..05aaa14 100644
--- a/include/media/MediaCodecBuffer.h
+++ b/include/media/MediaCodecBuffer.h
@@ -58,6 +58,8 @@
     sp<AMessage> meta();
     sp<AMessage> format();
 
+    virtual sp<MediaCodecBuffer> clone(const sp<AMessage> &format);
+
 private:
     MediaCodecBuffer() = delete;
 
diff --git a/include/media/OMXBuffer.h b/include/media/OMXBuffer.h
new file mode 100644
index 0000000..0322b73
--- /dev/null
+++ b/include/media/OMXBuffer.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _OMXBUFFER_H_
+#define _OMXBUFFER_H_
+
+#include <cutils/native_handle.h>
+#include <media/IOMX.h>
+#include <system/window.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class GraphicBuffer;
+class IMemory;
+class MediaCodecBuffer;
+class NativeHandle;
+class OMXNodeInstance;
+
+class OMXBuffer {
+public:
+    // sPreset is used in places where we are referring to a pre-registered
+    // buffer on a port. It has type kBufferTypePreset and mRangeLength of 0.
+    static OMXBuffer sPreset;
+
+    // Default constructor, constructs a buffer of type kBufferTypeInvalid.
+    OMXBuffer();
+
+    // Constructs a buffer of type kBufferTypePreset with mRangeLength set to
+    // |codecBuffer|'s size (or 0 if |codecBuffer| is NULL).
+    OMXBuffer(const sp<MediaCodecBuffer> &codecBuffer);
+
+    // Constructs a buffer of type kBufferTypeSharedMem.
+    OMXBuffer(const sp<IMemory> &mem, size_t allottedSize = 0);
+
+    // Constructs a buffer of type kBufferTypeANWBuffer.
+    OMXBuffer(const sp<GraphicBuffer> &gbuf);
+
+    // Constructs a buffer of type kBufferTypeNativeHandle.
+    OMXBuffer(const sp<NativeHandle> &handle);
+
+    // Parcelling/Un-parcelling.
+    status_t writeToParcel(Parcel *parcel) const;
+    status_t readFromParcel(const Parcel *parcel);
+
+    ~OMXBuffer();
+
+private:
+    friend class OMXNodeInstance;
+
+    enum BufferType {
+        kBufferTypeInvalid = 0,
+        kBufferTypePreset,
+        kBufferTypeSharedMem,
+        kBufferTypeANWBuffer,
+        kBufferTypeNativeHandle,
+    };
+
+    BufferType mBufferType;
+
+    // kBufferTypePreset
+    // If the port is operating in byte buffer mode, mRangeLength is the valid
+    // range length. Otherwise the range info should also be ignored.
+    OMX_U32 mRangeLength;
+
+    // kBufferTypeSharedMem
+    sp<IMemory> mMem;
+    OMX_U32 mAllottedSize;
+
+    // kBufferTypeANWBuffer
+    sp<GraphicBuffer> mGraphicBuffer;
+
+    // kBufferTypeNativeHandle
+    sp<NativeHandle> mNativeHandle;
+
+    OMXBuffer(const OMXBuffer &);
+    OMXBuffer &operator=(const OMXBuffer &);
+};
+
+}  // namespace android
+
+#endif  // _OMXBUFFER_H_
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index cdfe2c9..13ceeb6 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -81,7 +81,7 @@
         friend struct ACodec;
 
         Vector<IOMX::buffer_id> mBufferIDs;
-        Vector<sp<MediaCodecBuffer>> mBuffers;
+        Vector<sp<MediaCodecBuffer> > mBuffers;
 
         PortDescription();
         void addBuffer(IOMX::buffer_id id, const sp<MediaCodecBuffer> &buffer);
@@ -188,7 +188,6 @@
         sp<RefBase> mCodecRef;            // and a reference to the IMemory
 
         sp<GraphicBuffer> mGraphicBuffer;
-        sp<NativeHandle> mNativeHandle;
         int mFenceFd;
         FrameRenderTracker::Info *mRenderInfo;
 
@@ -200,8 +199,6 @@
         // Log error, if the current fence is not a read/write fence.
         void checkReadFence(const char *dbg);
         void checkWriteFence(const char *dbg);
-
-        sp<MediaCodecBuffer> alloc(const sp<AMessage> &format);
     };
 
     static const char *_asString(BufferInfo::Status s);
@@ -333,6 +330,8 @@
             uint32_t portIndex, IOMX::buffer_id bufferID,
             ssize_t *index = NULL);
 
+    status_t fillBuffer(BufferInfo *info);
+
     status_t setComponentRole(bool isEncoder, const char *mime);
 
     status_t configureCodec(const char *mime, const sp<AMessage> &msg);
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 8fc410d..f20c2cd 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -87,9 +87,10 @@
     int64_t mStartTimeUs;
     int16_t mMaxAmplitude;
     int64_t mPrevSampleTimeUs;
-    int64_t mFirstSampleTimeUs;
     int64_t mInitialReadTimeUs;
     int64_t mNumFramesReceived;
+    int64_t mNumFramesSkipped;
+    int64_t mNumFramesLost;
     int64_t mNumClientOwnedBuffers;
 
     List<MediaBuffer * > mBuffersReceived;
diff --git a/include/media/stagefright/CodecBase.h b/include/media/stagefright/CodecBase.h
index 1295b59..d8c43a4 100644
--- a/include/media/stagefright/CodecBase.h
+++ b/include/media/stagefright/CodecBase.h
@@ -45,7 +45,6 @@
         kWhatEOS                 = 'eos ',
         kWhatShutdownCompleted   = 'scom',
         kWhatFlushCompleted      = 'fcom',
-        kWhatOutputFormatChanged = 'outC',
         kWhatError               = 'erro',
         kWhatComponentAllocated  = 'cAll',
         kWhatComponentConfigured = 'cCon',
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 19d7047..89def5d 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -64,14 +64,15 @@
     };
 
     static const pid_t kNoPid = -1;
+    static const uid_t kNoUid = -1;
 
     static sp<MediaCodec> CreateByType(
             const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err = NULL,
-            pid_t pid = kNoPid);
+            pid_t pid = kNoPid, uid_t uid = kNoUid);
 
     static sp<MediaCodec> CreateByComponentName(
             const sp<ALooper> &looper, const AString &name, status_t *err = NULL,
-            pid_t pid = kNoPid);
+            pid_t pid = kNoPid, uid_t uid = kNoUid);
 
     static sp<PersistentSurface> CreatePersistentInputSurface();
 
@@ -246,7 +247,7 @@
         kFlagIsSecure                   = 64,
         kFlagSawMediaServerDie          = 128,
         kFlagIsEncoder                  = 256,
-        kFlagGatherCodecSpecificData    = 512,
+        // 512 skipped
         kFlagIsAsync                    = 1024,
         kFlagIsComponentAllocated       = 2048,
         kFlagPushBlankBuffersOnShutdown = 4096,
@@ -258,7 +259,6 @@
         sp<MediaCodecBuffer> mSecureData;
         sp<IMemory> mSharedEncryptedBuffer;
         sp<AMessage> mNotify;
-        sp<AMessage> mFormat;
         bool mOwnedByClient;
     };
 
@@ -287,6 +287,7 @@
     };
 
     State mState;
+    uid_t mUid;
     bool mReleasedByResourceManager;
     sp<ALooper> mLooper;
     sp<ALooper> mCodecLooper;
@@ -329,6 +330,7 @@
 
     List<size_t> mAvailPortBuffers[2];
     Vector<BufferInfo> mPortBuffers[2];
+    Vector<sp<MediaCodecBuffer>> mPortBufferArrays[2];
 
     int32_t mDequeueInputTimeoutGeneration;
     sp<AReplyToken> mDequeueInputReplyID;
@@ -345,7 +347,7 @@
     bool mHaveInputSurface;
     bool mHavePendingInputBuffers;
 
-    MediaCodec(const sp<ALooper> &looper, pid_t pid);
+    MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid);
 
     static sp<CodecBase> GetCodecBase(const AString &name, bool nameIsType = false);
 
diff --git a/include/media/stagefright/MediaFilter.h b/include/media/stagefright/MediaFilter.h
index 6aa87e8..0e39431 100644
--- a/include/media/stagefright/MediaFilter.h
+++ b/include/media/stagefright/MediaFilter.h
@@ -144,7 +144,6 @@
     void postFillThisBuffer(BufferInfo *info);
     void postDrainThisBuffer(BufferInfo *info);
     void postEOS();
-    void sendFormatChange();
     void requestFillEmptyInput();
     void processBuffers();
 
diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk
index 5ce1798..91cc902 100644
--- a/media/audioserver/Android.mk
+++ b/media/audioserver/Android.mk
@@ -13,7 +13,6 @@
 	liblog \
 	libmedia \
 	libmedialogservice \
-	libnbaio \
 	libradioservice \
 	libsoundtriggerservice \
 	libutils
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 80f78b6..4b0f6a2 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -5,3 +5,4 @@
     group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct
     ioprio rt 4
     writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+    onrestart restart audio-hal-2-0
\ No newline at end of file
diff --git a/media/img_utils/src/Android.mk b/media/img_utils/src/Android.mk
index c1f64ca..4c6fe70 100644
--- a/media/img_utils/src/Android.mk
+++ b/media/img_utils/src/Android.mk
@@ -35,11 +35,8 @@
 
 LOCAL_SHARED_LIBRARIES := \
   liblog \
-  libexpat \
   libutils \
   libcutils \
-  libcamera_metadata \
-  libcamera_client
 
 LOCAL_C_INCLUDES += \
   $(LOCAL_PATH)/../include \
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 8aed146..2dfdfde 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -2349,8 +2349,12 @@
 
     case EQ_PARAM_BAND_LEVEL:
         param2 = *pParamTemp;
-        if (param2 >= FIVEBAND_NUMBANDS) {
+        if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
             status = -EINVAL;
+            if (param2 < 0) {
+                android_errorWriteLog(0x534e4554, "32438598");
+                ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_BAND_LEVEL band %d", param2);
+            }
             break;
         }
         *(int16_t *)pValue = (int16_t)EqualizerGetBandLevel(pContext, param2);
@@ -2360,8 +2364,12 @@
 
     case EQ_PARAM_CENTER_FREQ:
         param2 = *pParamTemp;
-        if (param2 >= FIVEBAND_NUMBANDS) {
+        if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
             status = -EINVAL;
+            if (param2 < 0) {
+                android_errorWriteLog(0x534e4554, "32436341");
+                ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_CENTER_FREQ band %d", param2);
+            }
             break;
         }
         *(int32_t *)pValue = EqualizerGetCentreFrequency(pContext, param2);
@@ -2371,8 +2379,12 @@
 
     case EQ_PARAM_BAND_FREQ_RANGE:
         param2 = *pParamTemp;
-        if (param2 >= FIVEBAND_NUMBANDS) {
+        if (param2 < 0 || param2 >= FIVEBAND_NUMBANDS) {
             status = -EINVAL;
+            if (param2 < 0) {
+                android_errorWriteLog(0x534e4554, "32247948");
+                ALOGW("\tERROR Equalizer_getParameter() EQ_PARAM_BAND_FREQ_RANGE band %d", param2);
+            }
             break;
         }
         EqualizerGetBandFreqRange(pContext, param2, (uint32_t *)pValue, ((uint32_t *)pValue + 1));
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index ca96098..ba31cfa 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -65,6 +65,7 @@
     MediaProfiles.cpp \
     MediaResource.cpp \
     MediaResourcePolicy.cpp \
+    OMXBuffer.cpp \
     IEffect.cpp \
     IEffectClient.cpp \
     AudioEffect.cpp \
@@ -76,7 +77,7 @@
 LOCAL_SHARED_LIBRARIES := \
 	libui liblog libcutils libutils libbinder libsonivox libicuuc libicui18n libexpat \
         libcamera_client libstagefright_foundation \
-        libgui libdl libaudioutils libnbaio
+        libgui libdl libaudioutils
 
 LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder
 
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 33974e0..d45b12f 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -1175,14 +1175,14 @@
 
 status_t AudioSystem::startAudioSource(const struct audio_port_config *source,
                                        const audio_attributes_t *attributes,
-                                       audio_io_handle_t *handle)
+                                       audio_patch_handle_t *handle)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return PERMISSION_DENIED;
     return aps->startAudioSource(source, attributes, handle);
 }
 
-status_t AudioSystem::stopAudioSource(audio_io_handle_t handle)
+status_t AudioSystem::stopAudioSource(audio_patch_handle_t handle)
 {
     const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
     if (aps == 0) return PERMISSION_DENIED;
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 946da8a..2fb2da6 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -738,7 +738,7 @@
 
     virtual status_t startAudioSource(const struct audio_port_config *source,
                                       const audio_attributes_t *attributes,
-                                      audio_io_handle_t *handle)
+                                      audio_patch_handle_t *handle)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -755,11 +755,11 @@
         if (status != NO_ERROR) {
             return status;
         }
-        *handle = (audio_io_handle_t)reply.readInt32();
+        *handle = (audio_patch_handle_t)reply.readInt32();
         return status;
     }
 
-    virtual status_t stopAudioSource(audio_io_handle_t handle)
+    virtual status_t stopAudioSource(audio_patch_handle_t handle)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
@@ -1332,7 +1332,7 @@
             data.read(&source, sizeof(struct audio_port_config));
             audio_attributes_t attributes;
             data.read(&attributes, sizeof(audio_attributes_t));
-            audio_io_handle_t handle = {};
+            audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
             status_t status = startAudioSource(&source, &attributes, &handle);
             reply->writeInt32(status);
             reply->writeInt32(handle);
@@ -1341,7 +1341,7 @@
 
         case STOP_AUDIO_SOURCE: {
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
-            audio_io_handle_t handle = (audio_io_handle_t)data.readInt32();
+            audio_patch_handle_t handle = (audio_patch_handle_t) data.readInt32();
             status_t status = stopAudioSource(handle);
             reply->writeInt32(status);
             return NO_ERROR;
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 4bd1ab8..1a6d6b8 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -26,8 +26,8 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/openmax/OMX_IndexExt.h>
 #include <utils/NativeHandle.h>
+#include <media/OMXBuffer.h>
 
-#include <gui/IGraphicBufferProducer.h>
 #include <android/IGraphicBufferSource.h>
 #include <android/IOMXBufferSource.h>
 
@@ -45,7 +45,6 @@
     SET_CONFIG,
     ENABLE_NATIVE_BUFFERS,
     USE_BUFFER,
-    USE_GRAPHIC_BUFFER,
     CREATE_INPUT_SURFACE,
     SET_INPUT_SURFACE,
     STORE_META_DATA_IN_BUFFERS,
@@ -54,13 +53,10 @@
     FREE_BUFFER,
     FILL_BUFFER,
     EMPTY_BUFFER,
-    EMPTY_GRAPHIC_BUFFER,
     GET_EXTENSION_INDEX,
     OBSERVER_ON_MSG,
     GET_GRAPHIC_BUFFER_USAGE,
-    UPDATE_GRAPHIC_BUFFER_IN_META,
     CONFIGURE_VIDEO_TUNNEL_MODE,
-    UPDATE_NATIVE_HANDLE_IN_META,
     DISPATCH_MESSAGE,
     SET_QUIRKS,
 };
@@ -255,16 +251,19 @@
     }
 
     virtual status_t useBuffer(
-            OMX_U32 port_index, const sp<IMemory> &params,
-            buffer_id *buffer, OMX_U32 allottedSize) {
+            OMX_U32 port_index, const OMXBuffer &omxBuf, buffer_id *buffer) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
         data.writeInt32(port_index);
-        data.writeStrongBinder(IInterface::asBinder(params));
-        data.writeInt32(allottedSize);
+
+        status_t err = omxBuf.writeToParcel(&data);
+        if (err != OK) {
+            return err;
+        }
+
         remote()->transact(USE_BUFFER, data, &reply);
 
-        status_t err = reply.readInt32();
+        err = reply.readInt32();
         if (err != OK) {
             *buffer = 0;
 
@@ -276,59 +275,6 @@
         return err;
     }
 
-
-    virtual status_t useGraphicBuffer(
-            OMX_U32 port_index,
-            const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
-        data.writeInt32(port_index);
-        data.write(*graphicBuffer);
-        remote()->transact(USE_GRAPHIC_BUFFER, data, &reply);
-
-        status_t err = reply.readInt32();
-        if (err != OK) {
-            *buffer = 0;
-
-            return err;
-        }
-
-        *buffer = (buffer_id)reply.readInt32();
-
-        return err;
-    }
-
-    virtual status_t updateGraphicBufferInMeta(
-            OMX_U32 port_index,
-            const sp<GraphicBuffer> &graphicBuffer, buffer_id buffer) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
-        data.writeInt32(port_index);
-        data.write(*graphicBuffer);
-        data.writeInt32((int32_t)buffer);
-        remote()->transact(UPDATE_GRAPHIC_BUFFER_IN_META, data, &reply);
-
-        status_t err = reply.readInt32();
-        return err;
-    }
-
-    virtual status_t updateNativeHandleInMeta(
-            OMX_U32 port_index,
-            const sp<NativeHandle> &nativeHandle, buffer_id buffer) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
-        data.writeInt32(port_index);
-        data.writeInt32(nativeHandle != NULL);
-        if (nativeHandle != NULL) {
-            data.writeNativeHandle(nativeHandle->handle());
-        }
-        data.writeInt32((int32_t)buffer);
-        remote()->transact(UPDATE_NATIVE_HANDLE_IN_META, data, &reply);
-
-        status_t err = reply.readInt32();
-        return err;
-    }
-
     virtual status_t setInputSurface(
             const sp<IOMXBufferSource> &bufferSource) {
         Parcel data, reply;
@@ -439,10 +385,15 @@
         return reply.readInt32();
     }
 
-    virtual status_t fillBuffer(buffer_id buffer, int fenceFd) {
+    virtual status_t fillBuffer(
+            buffer_id buffer, const OMXBuffer &omxBuf, int fenceFd) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
         data.writeInt32((int32_t)buffer);
+        status_t err = omxBuf.writeToParcel(&data);
+        if (err != OK) {
+            return err;
+        }
         data.writeInt32(fenceFd >= 0);
         if (fenceFd >= 0) {
             data.writeFileDescriptor(fenceFd, true /* takeOwnership */);
@@ -453,14 +404,15 @@
     }
 
     virtual status_t emptyBuffer(
-            buffer_id buffer,
-            OMX_U32 range_offset, OMX_U32 range_length,
+            buffer_id buffer, const OMXBuffer &omxBuf,
             OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
         Parcel data, reply;
         data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
         data.writeInt32((int32_t)buffer);
-        data.writeInt32(range_offset);
-        data.writeInt32(range_length);
+        status_t err = omxBuf.writeToParcel(&data);
+        if (err != OK) {
+            return err;
+        }
         data.writeInt32(flags);
         data.writeInt64(timestamp);
         data.writeInt32(fenceFd >= 0);
@@ -472,25 +424,6 @@
         return reply.readInt32();
     }
 
-    virtual status_t emptyGraphicBuffer(
-            buffer_id buffer,
-            const sp<GraphicBuffer> &graphicBuffer, OMX_U32 flags,
-            OMX_TICKS timestamp, int fenceFd) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IOMXNode::getInterfaceDescriptor());
-        data.writeInt32((int32_t)buffer);
-        data.write(*graphicBuffer);
-        data.writeInt32(flags);
-        data.writeInt64(timestamp);
-        data.writeInt32(fenceFd >= 0);
-        if (fenceFd >= 0) {
-            data.writeFileDescriptor(fenceFd, true /* takeOwnership */);
-        }
-        remote()->transact(EMPTY_GRAPHIC_BUFFER, data, &reply);
-
-        return reply.readInt32();
-    }
-
     virtual status_t getExtensionIndex(
             const char *parameter_name,
             OMX_INDEXTYPE *index) {
@@ -770,18 +703,15 @@
             CHECK_OMX_INTERFACE(IOMXNode, data, reply);
 
             OMX_U32 port_index = data.readInt32();
-            sp<IMemory> params =
-                interface_cast<IMemory>(data.readStrongBinder());
-            OMX_U32 allottedSize = data.readInt32();
 
-            if (params == NULL) {
-                ALOGE("b/26392700");
-                reply->writeInt32(INVALID_OPERATION);
-                return NO_ERROR;
+            OMXBuffer omxBuf;
+            status_t err = omxBuf.readFromParcel(&data);
+            if (err != OK) {
+                return err;
             }
 
             buffer_id buffer;
-            status_t err = useBuffer(port_index, params, &buffer, allottedSize);
+            err = useBuffer(port_index, omxBuf, &buffer);
             reply->writeInt32(err);
 
             if (err == OK) {
@@ -791,60 +721,6 @@
             return NO_ERROR;
         }
 
-        case USE_GRAPHIC_BUFFER:
-        {
-            CHECK_OMX_INTERFACE(IOMXNode, data, reply);
-
-            OMX_U32 port_index = data.readInt32();
-            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
-            data.read(*graphicBuffer);
-
-            buffer_id buffer;
-            status_t err = useGraphicBuffer(
-                    port_index, graphicBuffer, &buffer);
-            reply->writeInt32(err);
-
-            if (err == OK) {
-                reply->writeInt32((int32_t)buffer);
-            }
-
-            return NO_ERROR;
-        }
-
-        case UPDATE_GRAPHIC_BUFFER_IN_META:
-        {
-            CHECK_OMX_INTERFACE(IOMXNode, data, reply);
-
-            OMX_U32 port_index = data.readInt32();
-            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
-            data.read(*graphicBuffer);
-            buffer_id buffer = (buffer_id)data.readInt32();
-
-            status_t err = updateGraphicBufferInMeta(
-                    port_index, graphicBuffer, buffer);
-            reply->writeInt32(err);
-
-            return NO_ERROR;
-        }
-
-        case UPDATE_NATIVE_HANDLE_IN_META:
-        {
-            CHECK_OMX_INTERFACE(IOMXNode, data, reply);
-
-            OMX_U32 port_index = data.readInt32();
-            native_handle *handle = NULL;
-            if (data.readInt32()) {
-                handle = data.readNativeHandle();
-            }
-            buffer_id buffer = (buffer_id)data.readInt32();
-
-            status_t err = updateNativeHandleInMeta(
-                    port_index, NativeHandle::create(handle, true /* ownshandle */), buffer);
-            reply->writeInt32(err);
-
-            return NO_ERROR;
-        }
-
         case SET_INPUT_SURFACE:
         {
             CHECK_OMX_INTERFACE(IOMXNode, data, reply);
@@ -956,9 +832,17 @@
             CHECK_OMX_INTERFACE(IOMXNode, data, reply);
 
             buffer_id buffer = (buffer_id)data.readInt32();
+
+            OMXBuffer omxBuf;
+            status_t err = omxBuf.readFromParcel(&data);
+            if (err != OK) {
+                return err;
+            }
+
             bool haveFence = data.readInt32();
             int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1;
-            reply->writeInt32(fillBuffer(buffer, fenceFd));
+
+            reply->writeInt32(fillBuffer(buffer, omxBuf, fenceFd));
 
             return NO_ERROR;
         }
@@ -968,31 +852,17 @@
             CHECK_OMX_INTERFACE(IOMXNode, data, reply);
 
             buffer_id buffer = (buffer_id)data.readInt32();
-            OMX_U32 range_offset = data.readInt32();
-            OMX_U32 range_length = data.readInt32();
+            OMXBuffer omxBuf;
+            status_t err = omxBuf.readFromParcel(&data);
+            if (err != OK) {
+                return err;
+            }
             OMX_U32 flags = data.readInt32();
             OMX_TICKS timestamp = data.readInt64();
             bool haveFence = data.readInt32();
             int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1;
             reply->writeInt32(emptyBuffer(
-                    buffer, range_offset, range_length, flags, timestamp, fenceFd));
-
-            return NO_ERROR;
-        }
-
-        case EMPTY_GRAPHIC_BUFFER:
-        {
-            CHECK_OMX_INTERFACE(IOMXNode, data, reply);
-
-            buffer_id buffer = (buffer_id)data.readInt32();
-            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
-            data.read(*graphicBuffer);
-            OMX_U32 flags = data.readInt32();
-            OMX_TICKS timestamp = data.readInt64();
-            bool haveFence = data.readInt32();
-            int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1;
-            reply->writeInt32(emptyGraphicBuffer(
-                    buffer, graphicBuffer, flags, timestamp, fenceFd));
+                    buffer, omxBuf, flags, timestamp, fenceFd));
 
             return NO_ERROR;
         }
diff --git a/media/libmedia/MediaCodecBuffer.cpp b/media/libmedia/MediaCodecBuffer.cpp
index 2d255be..2af31d0 100644
--- a/media/libmedia/MediaCodecBuffer.cpp
+++ b/media/libmedia/MediaCodecBuffer.cpp
@@ -80,4 +80,8 @@
     return mFormat;
 }
 
+sp<MediaCodecBuffer> MediaCodecBuffer::clone(const sp<AMessage> &format) {
+    return new MediaCodecBuffer(format, mBuffer);
+}
+
 }  // namespace android
diff --git a/media/libmedia/OMXBuffer.cpp b/media/libmedia/OMXBuffer.cpp
new file mode 100644
index 0000000..0931872
--- /dev/null
+++ b/media/libmedia/OMXBuffer.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMXBuffer"
+
+#include <media/MediaCodecBuffer.h>
+#include <media/OMXBuffer.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/NativeHandle.h>
+
+namespace android {
+
+//static
+OMXBuffer OMXBuffer::sPreset(static_cast<sp<MediaCodecBuffer> >(NULL));
+
+OMXBuffer::OMXBuffer()
+    : mBufferType(kBufferTypeInvalid) {
+}
+
+OMXBuffer::OMXBuffer(const sp<MediaCodecBuffer>& codecBuffer)
+    : mBufferType(kBufferTypePreset),
+      mRangeLength(codecBuffer != NULL ? codecBuffer->size() : 0) {
+}
+
+OMXBuffer::OMXBuffer(const sp<IMemory> &mem, size_t allottedSize)
+    : mBufferType(kBufferTypeSharedMem),
+      mMem(mem),
+      mAllottedSize(allottedSize ? : mem->size()) {
+}
+
+OMXBuffer::OMXBuffer(const sp<GraphicBuffer> &gbuf)
+    : mBufferType(kBufferTypeANWBuffer),
+      mGraphicBuffer(gbuf) {
+}
+
+OMXBuffer::OMXBuffer(const sp<NativeHandle> &handle)
+    : mBufferType(kBufferTypeNativeHandle),
+      mNativeHandle(handle) {
+}
+
+OMXBuffer::~OMXBuffer() {
+}
+
+status_t OMXBuffer::writeToParcel(Parcel *parcel) const {
+    parcel->writeInt32(mBufferType);
+
+    switch(mBufferType) {
+        case kBufferTypePreset:
+        {
+            return parcel->writeUint32(mRangeLength);
+        }
+
+        case kBufferTypeSharedMem:
+        {
+            status_t err = parcel->writeStrongBinder(IInterface::asBinder(mMem));
+            if (err != NO_ERROR) {
+                return err;
+            }
+            return parcel->writeUint32(mAllottedSize);
+        }
+
+        case kBufferTypeANWBuffer:
+        {
+            return parcel->write(*mGraphicBuffer);
+        }
+
+        case kBufferTypeNativeHandle:
+        {
+            return parcel->writeNativeHandle(mNativeHandle->handle());
+        }
+
+        default:
+            return BAD_VALUE;
+    }
+    return BAD_VALUE;
+}
+
+status_t OMXBuffer::readFromParcel(const Parcel *parcel) {
+    BufferType bufferType = (BufferType) parcel->readInt32();
+
+    switch(bufferType) {
+        case kBufferTypePreset:
+        {
+            mRangeLength = parcel->readUint32();
+            break;
+        }
+
+        case kBufferTypeSharedMem:
+        {
+            sp<IMemory> params = interface_cast<IMemory>(parcel->readStrongBinder());
+
+            mMem = params;
+            mAllottedSize = parcel->readUint32();
+            break;
+        }
+
+        case kBufferTypeANWBuffer:
+        {
+            sp<GraphicBuffer> buffer = new GraphicBuffer();
+
+            status_t err = parcel->read(*buffer);
+
+            if (err != OK) {
+                return err;
+            }
+
+            mGraphicBuffer = buffer;
+            break;
+        }
+
+        case kBufferTypeNativeHandle:
+        {
+            sp<NativeHandle> handle = NativeHandle::create(
+                    parcel->readNativeHandle(), true /* ownsHandle */);
+
+            mNativeHandle = handle;
+            break;
+        }
+
+        default:
+            return BAD_VALUE;
+    }
+
+    mBufferType = bufferType;
+    return OK;
+}
+
+} // namespace android
+
+
+
+
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 5270dea..97e7404 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -19,7 +19,6 @@
 
 LOCAL_SHARED_LIBRARIES :=       \
     libbinder                   \
-    libcamera_client            \
     libcrypto                   \
     libcutils                   \
     libdrmframework             \
@@ -29,14 +28,12 @@
     libmedia                    \
     libmediautils               \
     libmemunreachable           \
-    libsonivox                  \
     libstagefright              \
     libstagefright_foundation   \
     libstagefright_httplive     \
     libstagefright_omx          \
     libstagefright_wfd          \
     libutils                    \
-    libvorbisidec               \
 
 LOCAL_STATIC_LIBRARIES :=       \
     libstagefright_nuplayer     \
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 4554472..c0a5682 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -665,8 +665,8 @@
           } else {
               timeUs = mVideoLastDequeueTimeUs;
           }
-          readBuffer(trackType, timeUs, &actualTimeUs, formatChange);
-          readBuffer(counterpartType, -1, NULL, !formatChange);
+          readBuffer(trackType, timeUs, false /* precise */, &actualTimeUs, formatChange);
+          readBuffer(counterpartType, -1, false /* precise */, NULL, !formatChange);
           ALOGV("timeUs %lld actualTimeUs %lld", (long long)timeUs, (long long)actualTimeUs);
 
           break;
@@ -759,7 +759,7 @@
     CHECK(msg->findInt64("timeUs", &timeUs));
 
     int64_t subTimeUs;
-    readBuffer(type, timeUs, &subTimeUs);
+    readBuffer(type, timeUs, false /* precise */, &subTimeUs);
 
     int64_t delayUs = subTimeUs - timeUs;
     if (msg->what() == kWhatFetchSubtitleData) {
@@ -790,7 +790,7 @@
     }
 
     int64_t nextSubTimeUs;
-    readBuffer(type, -1, &nextSubTimeUs);
+    readBuffer(type, -1, false /* precise */, &nextSubTimeUs);
 
     sp<ABuffer> buffer;
     status_t dequeueStatus = packets->dequeueAccessUnit(&buffer);
@@ -1186,9 +1186,10 @@
     return INVALID_OPERATION;
 }
 
-status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) {
+status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs, bool precise) {
     sp<AMessage> msg = new AMessage(kWhatSeek, this);
     msg->setInt64("seekTimeUs", seekTimeUs);
+    msg->setInt32("precise", precise);
 
     sp<AMessage> response;
     status_t err = msg->postAndAwaitResponse(&response);
@@ -1201,10 +1202,12 @@
 
 void NuPlayer::GenericSource::onSeek(const sp<AMessage>& msg) {
     int64_t seekTimeUs;
+    int32_t precise;
     CHECK(msg->findInt64("seekTimeUs", &seekTimeUs));
+    CHECK(msg->findInt32("precise", &precise));
 
     sp<AMessage> response = new AMessage;
-    status_t err = doSeek(seekTimeUs);
+    status_t err = doSeek(seekTimeUs, precise);
     response->setInt32("err", err);
 
     sp<AReplyToken> replyID;
@@ -1212,7 +1215,7 @@
     response->postReply(replyID);
 }
 
-status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) {
+status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs, bool precise) {
     mBufferingMonitor->updateDequeuedBufferTime(-1ll);
 
     // If the Widevine source is stopped, do not attempt to read any
@@ -1222,10 +1225,12 @@
     }
     if (mVideoTrack.mSource != NULL) {
         int64_t actualTimeUs;
-        readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, &actualTimeUs);
+        readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, precise, &actualTimeUs);
 
-        seekTimeUs = actualTimeUs;
-        mVideoLastDequeueTimeUs = seekTimeUs;
+        if (!precise) {
+            seekTimeUs = actualTimeUs;
+        }
+        mVideoLastDequeueTimeUs = actualTimeUs;
     }
 
     if (mAudioTrack.mSource != NULL) {
@@ -1250,7 +1255,8 @@
 sp<ABuffer> NuPlayer::GenericSource::mediaBufferToABuffer(
         MediaBuffer* mb,
         media_track_type trackType,
-        int64_t /* seekTimeUs */,
+        int64_t seekTimeUs,
+        bool precise,
         int64_t *actualTimeUs) {
     bool audio = trackType == MEDIA_TRACK_TYPE_AUDIO;
     size_t outLength = mb->range_length();
@@ -1288,15 +1294,11 @@
     CHECK(mb->meta_data()->findInt64(kKeyTime, &timeUs));
     meta->setInt64("timeUs", timeUs);
 
-#if 0
-    // Temporarily disable pre-roll till we have a full solution to handle
-    // both single seek and continous seek gracefully.
-    if (seekTimeUs > timeUs) {
+    if (precise && seekTimeUs > timeUs) {
         sp<AMessage> extra = new AMessage;
         extra->setInt64("resume-at-mediaTimeUs", seekTimeUs);
         meta->setMessage("extra", extra);
     }
-#endif
 
     if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
         int32_t layerId;
@@ -1372,7 +1374,8 @@
 }
 
 void NuPlayer::GenericSource::readBuffer(
-        media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) {
+        media_track_type trackType, int64_t seekTimeUs, bool precise,
+        int64_t *actualTimeUs, bool formatChange) {
     // Do not read data if Widevine source is stopped
     if (mStopRead) {
         return;
@@ -1466,7 +1469,7 @@
             queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track);
 
             sp<ABuffer> buffer = mediaBufferToABuffer(
-                    mbuf, trackType, seekTimeUs,
+                    mbuf, trackType, seekTimeUs, precise,
                     numBuffers == 0 ? actualTimeUs : NULL);
             track->mPackets->queueAccessUnit(buffer);
             formatChange = false;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 0957778..f9db6a5 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -71,7 +71,7 @@
     virtual sp<AMessage> getTrackInfo(size_t trackIndex) const;
     virtual ssize_t getSelectedTrack(media_track_type type) const;
     virtual status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs);
-    virtual status_t seekTo(int64_t seekTimeUs);
+    virtual status_t seekTo(int64_t seekTimeUs, bool precise = false);
 
     virtual status_t setBuffers(bool audio, Vector<MediaBuffer *> &buffers);
 
@@ -258,7 +258,7 @@
     status_t doSelectTrack(size_t trackIndex, bool select, int64_t timeUs);
 
     void onSeek(const sp<AMessage>& msg);
-    status_t doSeek(int64_t seekTimeUs);
+    status_t doSeek(int64_t seekTimeUs, bool precise);
 
     void onPrepareAsync();
 
@@ -278,13 +278,15 @@
             MediaBuffer *mbuf,
             media_track_type trackType,
             int64_t seekTimeUs,
+            bool precise,
             int64_t *actualTimeUs = NULL);
 
     void postReadBuffer(media_track_type trackType);
     void onReadBuffer(const sp<AMessage>& msg);
     void readBuffer(
             media_track_type trackType,
-            int64_t seekTimeUs = -1ll, int64_t *actualTimeUs = NULL, bool formatChange = false);
+            int64_t seekTimeUs = -1ll, bool precise = false,
+            int64_t *actualTimeUs = NULL, bool formatChange = false);
 
     void queueDiscontinuityIfNeeded(
             bool seeking, bool formatChange, media_track_type trackType, Track *track);
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index ebba93c..1a6a233 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -214,8 +214,8 @@
     return (err == OK || err == BAD_VALUE) ? (status_t)OK : err;
 }
 
-status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) {
-    return mLiveSession->seekTo(seekTimeUs);
+status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs, bool precise) {
+    return mLiveSession->seekTo(seekTimeUs, precise);
 }
 
 void NuPlayer::HTTPLiveSource::pollForRawData(
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index 574937d..be3b4f0 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -47,7 +47,7 @@
     virtual sp<AMessage> getTrackInfo(size_t trackIndex) const;
     virtual ssize_t getSelectedTrack(media_track_type /* type */) const;
     virtual status_t selectTrack(size_t trackIndex, bool select, int64_t timeUs);
-    virtual status_t seekTo(int64_t seekTimeUs);
+    virtual status_t seekTo(int64_t seekTimeUs, bool precise = false);
 
 protected:
     virtual ~HTTPLiveSource();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 651cb67..cbc32b1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1644,7 +1644,7 @@
         } else {
             mSource->setOffloadAudio(false /* offload */);
 
-            *decoder = new Decoder(notify, mSource, mPID, mRenderer);
+            *decoder = new Decoder(notify, mSource, mPID, mUID, mRenderer);
         }
     } else {
         sp<AMessage> notify = new AMessage(kWhatVideoNotify, this);
@@ -1652,7 +1652,7 @@
         notify->setInt32("generation", mVideoDecoderGeneration);
 
         *decoder = new Decoder(
-                notify, mSource, mPID, mRenderer, mSurface, mCCDecoder);
+                notify, mSource, mPID, mUID, mRenderer, mSurface, mCCDecoder);
 
         // enable FRC if high-quality AV sync is requested, even if not
         // directly queuing to display, as this will even improve textureview
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 822f154..6974207 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -58,6 +58,7 @@
         const sp<AMessage> &notify,
         const sp<Source> &source,
         pid_t pid,
+        uid_t uid,
         const sp<Renderer> &renderer,
         const sp<Surface> &surface,
         const sp<CCDecoder> &ccDecoder)
@@ -67,6 +68,7 @@
       mRenderer(renderer),
       mCCDecoder(ccDecoder),
       mPid(pid),
+      mUid(uid),
       mSkipRenderingUntilMediaTimeUs(-1ll),
       mNumFramesTotal(0ll),
       mNumInputFramesDropped(0ll),
@@ -266,7 +268,7 @@
     ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), mSurface.get());
 
     mCodec = MediaCodec::CreateByType(
-            mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid);
+            mCodecLooper, mime.c_str(), false /* encoder */, NULL /* err */, mPid, mUid);
     int32_t secure = 0;
     if (format->findInt32("secure", &secure) && secure != 0) {
         if (mCodec != NULL) {
@@ -275,7 +277,7 @@
             mCodec->release();
             ALOGI("[%s] creating", mComponentName.c_str());
             mCodec = MediaCodec::CreateByComponentName(
-                    mCodecLooper, mComponentName.c_str(), NULL /* err */, mPid);
+                    mCodecLooper, mComponentName.c_str(), NULL /* err */, mPid, mUid);
         }
     }
     if (mCodec == NULL) {
@@ -755,7 +757,7 @@
 
 status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
     sp<ABuffer> accessUnit;
-    bool dropAccessUnit;
+    bool dropAccessUnit = true;
     do {
         status_t err = mSource->dequeueAccessUnit(mIsAudio, &accessUnit);
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 8186862..a576ae5 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -29,6 +29,7 @@
     Decoder(const sp<AMessage> &notify,
             const sp<Source> &source,
             pid_t pid,
+            uid_t uid,
             const sp<Renderer> &renderer = NULL,
             const sp<Surface> &surface = NULL,
             const sp<CCDecoder> &ccDecoder = NULL);
@@ -85,6 +86,7 @@
     Vector<size_t> mDequeuedInputBuffers;
 
     const pid_t mPid;
+    const uid_t mUid;
     int64_t mSkipRenderingUntilMediaTimeUs;
     int64_t mNumFramesTotal;
     int64_t mNumInputFramesDropped;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 3a96138..b62fbfd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -104,7 +104,7 @@
         return INVALID_OPERATION;
     }
 
-    virtual status_t seekTo(int64_t /* seekTimeUs */) {
+    virtual status_t seekTo(int64_t /* seekTimeUs */, bool /* precise */ = false) {
         return INVALID_OPERATION;
     }
 
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 7ce909d..f430f03 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -279,10 +279,11 @@
     return OK;
 }
 
-status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) {
+status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs, bool precise) {
     sp<AMessage> msg = new AMessage(kWhatPerformSeek, this);
     msg->setInt32("generation", ++mSeekGeneration);
     msg->setInt64("timeUs", seekTimeUs);
+    msg->setInt32("precise", precise);
 
     sp<AMessage> response;
     status_t err = msg->postAndAwaitResponse(&response);
@@ -465,8 +466,11 @@
         }
 
         int64_t seekTimeUs;
+        int32_t precise;
         CHECK(msg->findInt64("timeUs", &seekTimeUs));
+        CHECK(msg->findInt32("precise", &precise));
 
+        // TODO: add "precise" to performSeek.
         performSeek(seekTimeUs);
         return;
     } else if (msg->what() == kWhatPollBuffering) {
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
index c7834ef..f2d9cc0 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.h
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -49,7 +49,7 @@
     virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
 
     virtual status_t getDuration(int64_t *durationUs);
-    virtual status_t seekTo(int64_t seekTimeUs);
+    virtual status_t seekTo(int64_t seekTimeUs, bool precise = false);
 
     void onMessageReceived(const sp<AMessage> &msg);
 
diff --git a/media/libnbaio/Android.bp b/media/libnbaio/Android.bp
index 45ad16f..615b541 100644
--- a/media/libnbaio/Android.bp
+++ b/media/libnbaio/Android.bp
@@ -22,7 +22,6 @@
     // static_libs: ["libsndfile"],
 
     shared_libs: [
-        "libaudiohal",
         "libaudioutils",
         "libbinder",
         "libcutils",
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ed29859..094f5cc 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -44,6 +44,7 @@
 #include <media/stagefright/PersistentSurface.h>
 #include <media/stagefright/SurfaceUtils.h>
 #include <media/hardware/HardwareAPI.h>
+#include <media/OMXBuffer.h>
 
 #include <OMX_AudioExt.h>
 #include <OMX_VideoExt.h>
@@ -796,7 +797,7 @@
 
     status_t err;
     if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
-        if (storingMetadataInDecodedBuffers()) {
+        if (storingMetadataInDecodedBuffers() && !mLegacyAdaptiveExperiment) {
             err = allocateOutputMetadataBuffers();
         } else {
             err = allocateOutputBuffersFromNativeWindow();
@@ -866,7 +867,8 @@
             size_t totalSize = def.nBufferCountActual * (alignedSize + alignedConvSize);
             mDealer[portIndex] = new MemoryDealer(totalSize, "ACodec");
 
-            const sp<AMessage> &format = portIndex == kPortIndexInput ? mInputFormat : mOutputFormat;
+            const sp<AMessage> &format =
+                    portIndex == kPortIndexInput ? mInputFormat : mOutputFormat;
             for (OMX_U32 i = 0; i < def.nBufferCountActual && err == OK; ++i) {
                 sp<IMemory> mem = mDealer[portIndex]->allocate(bufSize);
                 if (mem == NULL || mem->pointer() == NULL) {
@@ -892,8 +894,8 @@
                             : new SecureBuffer(format, native_handle, bufSize);
                     info.mCodecData = info.mData;
                 } else {
-                    err = mOMXNode->useBuffer(
-                            portIndex, mem, &info.mBufferID, allottedSize);
+                    err = mOMXNode->useBuffer(portIndex,
+                            OMXBuffer(mem, allottedSize), &info.mBufferID);
                 }
 
                 if (mem != NULL) {
@@ -1084,6 +1086,11 @@
 }
 
 status_t ACodec::allocateOutputBuffersFromNativeWindow() {
+    // This method only handles the non-metadata mode, or legacy metadata mode
+    // (where the headers for each buffer id will be fixed). Non-legacy metadata
+    // mode shouldn't go through this path.
+    CHECK(!storingMetadataInDecodedBuffers() || mLegacyAdaptiveExperiment);
+
     OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
     status_t err = configureOutputBuffersFromNativeWindow(
             &bufferCount, &bufferSize, &minUndequeuedBuffers, true /* preregister */);
@@ -1091,10 +1098,8 @@
         return err;
     mNumUndequeuedBuffers = minUndequeuedBuffers;
 
-    if (!storingMetadataInDecodedBuffers()) {
-        static_cast<Surface*>(mNativeWindow.get())
-                ->getIGraphicBufferProducer()->allowAllocation(true);
-    }
+    static_cast<Surface*>(mNativeWindow.get())
+            ->getIGraphicBufferProducer()->allowAllocation(true);
 
     ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
          "output port",
@@ -1117,11 +1122,19 @@
         info.mIsReadFence = false;
         info.mRenderInfo = NULL;
         info.mGraphicBuffer = graphicBuffer;
+
+        // TODO: We shouln't need to create MediaCodecBuffer. In metadata mode
+        //       OMX doesn't use the shared memory buffer, but some code still
+        //       access info.mData. Create an ABuffer as a placeholder.
+        if (storingMetadataInDecodedBuffers()) {
+            info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));
+            info.mCodecData = info.mData;
+        }
+
         mBuffers[kPortIndexOutput].push(info);
 
         IOMX::buffer_id bufferId;
-        err = mOMXNode->useGraphicBuffer(
-                kPortIndexOutput, graphicBuffer, &bufferId);
+        err = mOMXNode->useBuffer(kPortIndexOutput, graphicBuffer, &bufferId);
         if (err != 0) {
             ALOGE("registering GraphicBuffer %u with OMX IL component failed: "
                  "%d", i, err);
@@ -1138,9 +1151,9 @@
     OMX_U32 cancelStart;
     OMX_U32 cancelEnd;
 
-    if (err != 0) {
+    if (err != 0 || storingMetadataInDecodedBuffers()) {
         // If an error occurred while dequeuing we need to cancel any buffers
-        // that were dequeued.
+        // that were dequeued. Also cancel all if we're in legacy metadata mode.
         cancelStart = 0;
         cancelEnd = mBuffers[kPortIndexOutput].size();
     } else {
@@ -1159,19 +1172,23 @@
         }
     }
 
-    if (!storingMetadataInDecodedBuffers()) {
-        static_cast<Surface*>(mNativeWindow.get())
-                ->getIGraphicBufferProducer()->allowAllocation(false);
+    static_cast<Surface*>(mNativeWindow.get())
+            ->getIGraphicBufferProducer()->allowAllocation(false);
+
+    if (storingMetadataInDecodedBuffers()) {
+        mMetadataBuffersToSubmit = bufferCount - minUndequeuedBuffers;
     }
 
     return err;
 }
 
 status_t ACodec::allocateOutputMetadataBuffers() {
+    CHECK(storingMetadataInDecodedBuffers() && !mLegacyAdaptiveExperiment);
+
     OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
     status_t err = configureOutputBuffersFromNativeWindow(
             &bufferCount, &bufferSize, &minUndequeuedBuffers,
-            mLegacyAdaptiveExperiment /* preregister */);
+            false /* preregister */);
     if (err != 0)
         return err;
     mNumUndequeuedBuffers = minUndequeuedBuffers;
@@ -1184,7 +1201,6 @@
     size_t totalSize = bufferCount * align(bufSize, MemoryDealer::getAllocationAlignment());
     mDealer[kPortIndexOutput] = new MemoryDealer(totalSize, "ACodec");
 
-    // Dequeue buffers and send them to OMX
     for (OMX_U32 i = 0; i < bufferCount; i++) {
         BufferInfo info;
         info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
@@ -1205,57 +1221,13 @@
         info.mCodecData = info.mData;
         info.mCodecRef = mem;
 
-        err = mOMXNode->useBuffer(
-                kPortIndexOutput, mem, &info.mBufferID, mem->size());
+        err = mOMXNode->useBuffer(kPortIndexOutput, mem, &info.mBufferID);
         mBuffers[kPortIndexOutput].push(info);
 
         ALOGV("[%s] allocated meta buffer with ID %u (pointer = %p)",
              mComponentName.c_str(), info.mBufferID, mem->pointer());
     }
 
-    if (mLegacyAdaptiveExperiment) {
-        // preallocate and preregister buffers
-        static_cast<Surface *>(mNativeWindow.get())
-                ->getIGraphicBufferProducer()->allowAllocation(true);
-
-        ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
-             "output port",
-             mComponentName.c_str(), bufferCount, bufferSize);
-
-        // Dequeue buffers then cancel them all
-        for (OMX_U32 i = 0; i < bufferCount; i++) {
-            BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
-
-            ANativeWindowBuffer *buf;
-            int fenceFd;
-            err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd);
-            if (err != 0) {
-                ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
-                break;
-            }
-
-            sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
-            mOMXNode->updateGraphicBufferInMeta(
-                    kPortIndexOutput, graphicBuffer, info->mBufferID);
-            info->mStatus = BufferInfo::OWNED_BY_US;
-            info->setWriteFence(fenceFd, "allocateOutputMetadataBuffers for legacy");
-            info->mGraphicBuffer = graphicBuffer;
-        }
-
-        for (OMX_U32 i = 0; i < mBuffers[kPortIndexOutput].size(); i++) {
-            BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
-            if (info->mStatus == BufferInfo::OWNED_BY_US) {
-                status_t error = cancelBufferToNativeWindow(info);
-                if (err == OK) {
-                    err = error;
-                }
-            }
-        }
-
-        static_cast<Surface*>(mNativeWindow.get())
-                ->getIGraphicBufferProducer()->allowAllocation(false);
-    }
-
     mMetadataBuffersToSubmit = bufferCount - minUndequeuedBuffers;
     return err;
 }
@@ -1275,13 +1247,7 @@
 
     --mMetadataBuffersToSubmit;
     info->checkWriteFence("submitOutputMetadataBuffer");
-    status_t err = mOMXNode->fillBuffer(info->mBufferID, info->mFenceFd);
-    info->mFenceFd = -1;
-    if (err == OK) {
-        info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
-    }
-
-    return err;
+    return fillBuffer(info);
 }
 
 status_t ACodec::waitForFence(int fd, const char *dbg ) {
@@ -1466,6 +1432,12 @@
     // while loop above does not complete
     CHECK(storingMetadataInDecodedBuffers());
 
+    if (storingMetadataInDecodedBuffers() && mLegacyAdaptiveExperiment) {
+        // If we're here while running legacy experiment, we dequeued some
+        // unrecognized buffers, and the experiment can't continue.
+        ALOGE("Legacy experiment failed, drop back to metadata mode");
+        mLegacyAdaptiveExperiment = false;
+    }
     // discard buffer in LRU info and replace with new buffer
     oldest->mGraphicBuffer = new GraphicBuffer(buf, false);
     oldest->mStatus = BufferInfo::OWNED_BY_US;
@@ -1473,25 +1445,22 @@
     mRenderTracker.untrackFrame(oldest->mRenderInfo);
     oldest->mRenderInfo = NULL;
 
-    mOMXNode->updateGraphicBufferInMeta(
-            kPortIndexOutput, oldest->mGraphicBuffer, oldest->mBufferID);
-
     if (mOutputMetadataType == kMetadataBufferTypeGrallocSource) {
         VideoGrallocMetadata *grallocMeta =
-            reinterpret_cast<VideoGrallocMetadata *>(oldest->mData->data());
+            reinterpret_cast<VideoGrallocMetadata *>(oldest->mCodecData->base());
         ALOGV("replaced oldest buffer #%u with age %u (%p/%p stored in %p)",
                 (unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
                 mDequeueCounter - oldest->mDequeuedAt,
                 (void *)(uintptr_t)grallocMeta->pHandle,
-                oldest->mGraphicBuffer->handle, oldest->mData->data());
+                oldest->mGraphicBuffer->handle, oldest->mCodecData->base());
     } else if (mOutputMetadataType == kMetadataBufferTypeANWBuffer) {
         VideoNativeMetadata *nativeMeta =
-            reinterpret_cast<VideoNativeMetadata *>(oldest->mData->data());
+            reinterpret_cast<VideoNativeMetadata *>(oldest->mCodecData->base());
         ALOGV("replaced oldest buffer #%u with age %u (%p/%p stored in %p)",
                 (unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
                 mDequeueCounter - oldest->mDequeuedAt,
                 (void *)(uintptr_t)nativeMeta->pBuffer,
-                oldest->mGraphicBuffer->getNativeBuffer(), oldest->mData->data());
+                oldest->mGraphicBuffer->getNativeBuffer(), oldest->mCodecData->base());
     }
 
     updateRenderInfoForDequeuedBuffer(buf, fenceFd, oldest);
@@ -1541,9 +1510,9 @@
     // there should not be any fences in the metadata
     MetadataBufferType type =
         portIndex == kPortIndexOutput ? mOutputMetadataType : mInputMetadataType;
-    if (type == kMetadataBufferTypeANWBuffer && info->mData != NULL
-            && info->mData->size() >= sizeof(VideoNativeMetadata)) {
-        int fenceFd = ((VideoNativeMetadata *)info->mData->data())->nFenceFd;
+    if (type == kMetadataBufferTypeANWBuffer && info->mCodecData != NULL
+            && info->mCodecData->size() >= sizeof(VideoNativeMetadata)) {
+        int fenceFd = ((VideoNativeMetadata *)info->mCodecData->base())->nFenceFd;
         if (fenceFd >= 0) {
             ALOGW("unreleased fence (%d) in %s metadata buffer %zu",
                     fenceFd, portIndex == kPortIndexInput ? "input" : "output", i);
@@ -1598,6 +1567,23 @@
     return NULL;
 }
 
+status_t ACodec::fillBuffer(BufferInfo *info) {
+    status_t err;
+    if (!storingMetadataInDecodedBuffers() || mLegacyAdaptiveExperiment) {
+        err = mOMXNode->fillBuffer(
+            info->mBufferID, OMXBuffer::sPreset, info->mFenceFd);
+    } else {
+        err = mOMXNode->fillBuffer(
+            info->mBufferID, info->mGraphicBuffer, info->mFenceFd);
+    }
+
+    info->mFenceFd = -1;
+    if (err == OK) {
+        info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+    }
+    return err;
+}
+
 status_t ACodec::setComponentRole(
         bool isEncoder, const char *mime) {
     const char *role = GetComponentRole(isEncoder, mime);
@@ -2190,7 +2176,6 @@
 
     // NOTE: both mBaseOutputFormat and mOutputFormat are outputFormat to signal first frame.
     mBaseOutputFormat = outputFormat;
-    // trigger a kWhatOutputFormatChanged msg on first buffer
     mLastOutputFormat.clear();
 
     err = getPortFormat(kPortIndexInput, inputFormat);
@@ -5188,11 +5173,6 @@
         mSkipCutBuffer = new SkipCutBuffer(mEncoderDelay, mEncoderPadding, channelCount);
     }
 
-    sp<AMessage> notify = mNotify->dup();
-    notify->setInt32("what", kWhatOutputFormatChanged);
-    notify->setMessage("format", mOutputFormat);
-    notify->post();
-
     // mLastOutputFormat is not used when tunneled; doing this just to stay consistent
     mLastOutputFormat = mOutputFormat;
 }
@@ -5572,8 +5552,8 @@
     notify->setInt32("what", CodecBase::kWhatFillThisBuffer);
     notify->setInt32("buffer-id", info->mBufferID);
 
-    info->mData->meta()->clear();
-    notify->setObject("buffer", info->mData);
+    notify->setObject("buffer", info->mData->clone(mCodec->mInputFormat));
+    info->mData.clear();
 
     sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec);
     reply->setInt32("buffer-id", info->mBufferID);
@@ -5606,8 +5586,6 @@
                  mCodec->mComponentName.c_str(), err);
             eos = true;
         }
-
-        buffer.clear();
     } else {
         buffer = static_cast<MediaCodecBuffer *>(obj.get());
     }
@@ -5628,6 +5606,7 @@
     }
 
     info->mStatus = BufferInfo::OWNED_BY_US;
+    info->mData = buffer;
 
     switch (mode) {
         case KEEP_BUFFERS:
@@ -5672,11 +5651,12 @@
                     flags |= OMX_BUFFERFLAG_EOS;
                 }
 
-                if (buffer != info->mCodecData) {
+                size_t size = buffer->size();
+                if (buffer->base() != info->mCodecData->base()) {
                     ALOGV("[%s] Needs to copy input data for buffer %u. (%p != %p)",
                          mCodec->mComponentName.c_str(),
                          bufferID,
-                         buffer.get(), info->mCodecData.get());
+                         buffer->base(), info->mCodecData->base());
 
                     sp<DataConverter> converter = mCodec->mConverter[kPortIndexInput];
                     if (converter == NULL || isCSD) {
@@ -5687,6 +5667,9 @@
                         mCodec->signalError(OMX_ErrorUndefined, err);
                         return;
                     }
+                    size = info->mCodecData->size();
+                } else {
+                    info->mCodecData->setRange(0, size);
                 }
 
                 if (flags & OMX_BUFFERFLAG_CODECCONFIG) {
@@ -5729,25 +5712,29 @@
                 status_t err2 = OK;
                 switch (metaType) {
                 case kMetadataBufferTypeInvalid:
+                    {
+                        err2 = mCodec->mOMXNode->emptyBuffer(
+                            bufferID, info->mCodecData, flags, timeUs, info->mFenceFd);
+                    }
                     break;
 #ifndef OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
                 case kMetadataBufferTypeNativeHandleSource:
                     if (info->mCodecData->size() >= sizeof(VideoNativeHandleMetadata)) {
                         VideoNativeHandleMetadata *vnhmd =
                             (VideoNativeHandleMetadata*)info->mCodecData->base();
-                        err2 = mCodec->mOMXNode->updateNativeHandleInMeta(
-                                mCodec->kPortIndexInput,
-                                NativeHandle::create(vnhmd->pHandle, false /* ownsHandle */),
-                                bufferID);
+                        sp<NativeHandle> handle = NativeHandle::create(
+                                vnhmd->pHandle, false /* ownsHandle */);
+                        err2 = mCodec->mOMXNode->emptyBuffer(
+                            bufferID, handle, flags, timeUs, info->mFenceFd);
                     }
                     break;
                 case kMetadataBufferTypeANWBuffer:
                     if (info->mCodecData->size() >= sizeof(VideoNativeMetadata)) {
                         VideoNativeMetadata *vnmd = (VideoNativeMetadata*)info->mCodecData->base();
-                        err2 = mCodec->mOMXNode->updateGraphicBufferInMeta(
-                                mCodec->kPortIndexInput,
-                                new GraphicBuffer(vnmd->pBuffer, false /* keepOwnership */),
-                                bufferID);
+                        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+                                vnmd->pBuffer, false /* keepOwnership */);
+                        err2 = mCodec->mOMXNode->emptyBuffer(
+                            bufferID, graphicBuffer, flags, timeUs, info->mFenceFd);
                     }
                     break;
 #endif
@@ -5759,21 +5746,14 @@
                     break;
                 }
 
-                if (err2 == OK) {
-                    err2 = mCodec->mOMXNode->emptyBuffer(
-                        bufferID,
-                        0,
-                        info->mCodecData->size(),
-                        flags,
-                        timeUs,
-                        info->mFenceFd);
-                }
                 info->mFenceFd = -1;
                 if (err2 != OK) {
                     mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err2));
                     return;
                 }
                 info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+                // Hold the reference while component is using the buffer.
+                info->mData = buffer;
 
                 if (!eos && err == OK) {
                     getMoreInputDataIfPossible();
@@ -5798,12 +5778,7 @@
 
                 info->checkReadFence("onInputBufferFilled");
                 status_t err2 = mCodec->mOMXNode->emptyBuffer(
-                        bufferID,
-                        0,
-                        0,
-                        OMX_BUFFERFLAG_EOS,
-                        0,
-                        info->mFenceFd);
+                        bufferID, OMXBuffer::sPreset, OMX_BUFFERFLAG_EOS, 0, info->mFenceFd);
                 info->mFenceFd = -1;
                 if (err2 != OK) {
                     mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err2));
@@ -5927,20 +5902,17 @@
                 ALOGV("[%s] calling fillBuffer %u",
                      mCodec->mComponentName.c_str(), info->mBufferID);
 
-                err = mCodec->mOMXNode->fillBuffer(info->mBufferID, info->mFenceFd);
-                info->mFenceFd = -1;
+                err = mCodec->fillBuffer(info);
                 if (err != OK) {
                     mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
                     return true;
                 }
-
-                info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
                 break;
             }
 
             sp<AMessage> reply =
                 new AMessage(kWhatOutputBufferDrained, mCodec);
-            sp<MediaCodecBuffer> buffer = info->mData;
+            sp<MediaCodecBuffer> buffer = info->mData->clone(mCodec->mOutputFormat);
 
             if (mCodec->mOutputFormat != mCodec->mLastOutputFormat && rangeLength > 0) {
                 // pretend that output format has changed on the first frame (we used to do this)
@@ -5971,7 +5943,7 @@
                 buffer->meta()->setPointer("handle", handle);
                 buffer->meta()->setInt32("rangeOffset", rangeOffset);
                 buffer->meta()->setInt32("rangeLength", rangeLength);
-            } else if (buffer == info->mCodecData) {
+            } else if (buffer->base() == info->mCodecData->base()) {
                 buffer->setRange(rangeOffset, rangeLength);
             } else {
                 info->mCodecData->setRange(rangeOffset, rangeLength);
@@ -6000,6 +5972,7 @@
             notify->setInt32("what", CodecBase::kWhatDrainThisBuffer);
             notify->setInt32("buffer-id", info->mBufferID);
             notify->setObject("buffer", buffer);
+            info->mData.clear();
             notify->setInt32("flags", flags);
 
             reply->setInt32("buffer-id", info->mBufferID);
@@ -6056,6 +6029,7 @@
         mCodec->signalError(OMX_ErrorUndefined, FAILED_TRANSACTION);
         return;
     }
+    info->mData = buffer;
 
     android_native_rect_t crop;
     if (msg->findRect("crop", &crop.left, &crop.top, &crop.right, &crop.bottom)
@@ -6155,12 +6129,8 @@
                     ALOGV("[%s] calling fillBuffer %u",
                          mCodec->mComponentName.c_str(), info->mBufferID);
                     info->checkWriteFence("onOutputBufferDrained::RESUBMIT_BUFFERS");
-                    status_t err = mCodec->mOMXNode->fillBuffer(
-                            info->mBufferID, info->mFenceFd);
-                    info->mFenceFd = -1;
-                    if (err == OK) {
-                        info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
-                    } else {
+                    status_t err = mCodec->fillBuffer(info);
+                    if (err != OK) {
                         mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
                     }
                 }
@@ -6960,14 +6930,11 @@
         ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID);
 
         info->checkWriteFence("submitRegularOutputBuffers");
-        status_t err = mCodec->mOMXNode->fillBuffer(info->mBufferID, info->mFenceFd);
-        info->mFenceFd = -1;
+        status_t err = mCodec->fillBuffer(info);
         if (err != OK) {
             failed = true;
             break;
         }
-
-        info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
     }
 
     if (failed) {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 2c741db..4eacff5 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -72,6 +72,8 @@
         $(TOP)/external/flac/include \
         $(TOP)/external/tremolo \
         $(TOP)/external/libvpx/libwebm \
+        $(TOP)/external/icu/icu4c/source/common \
+        $(TOP)/external/icu/icu4c/source/i18n \
         $(TOP)/system/netd/include \
         $(call include-path-for, audio-utils)
 
@@ -84,23 +86,15 @@
         libdrmframework \
         libexpat \
         libgui \
-        libicui18n \
-        libicuuc \
         liblog \
         libmedia \
         libmediautils \
         libnetd_client \
-        libopus \
         libsonivox \
-        libssl \
         libstagefright_omx \
-        libstagefright_yuv \
-        libsync \
         libui \
         libutils \
         libvorbisidec \
-        libz \
-        libpowermanager
 
 LOCAL_STATIC_LIBRARIES := \
         libstagefright_color_conversion \
@@ -118,8 +112,6 @@
         libmedia_helper \
 
 LOCAL_SHARED_LIBRARIES += \
-        libstagefright_enc_common \
-        libstagefright_avc_common \
         libstagefright_foundation \
         libdl \
         libRScpp \
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 790c6da..4ccd2d0 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -60,9 +60,10 @@
       mStartTimeUs(0),
       mMaxAmplitude(0),
       mPrevSampleTimeUs(0),
-      mFirstSampleTimeUs(-1ll),
       mInitialReadTimeUs(0),
       mNumFramesReceived(0),
+      mNumFramesSkipped(0),
+      mNumFramesLost(0),
       mNumClientOwnedBuffers(0) {
     ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u",
             sampleRate, outSampleRate, channelCount);
@@ -277,12 +278,8 @@
     }
 
     if (mSampleRate != mOutSampleRate) {
-        if (mFirstSampleTimeUs < 0) {
-            mFirstSampleTimeUs = timeUs;
-        }
-        timeUs = mFirstSampleTimeUs + (timeUs - mFirstSampleTimeUs)
-                * (int64_t)mSampleRate / (int64_t)mOutSampleRate;
-        buffer->meta_data()->setInt64(kKeyTime, timeUs);
+            timeUs *= (int64_t)mSampleRate / (int64_t)mOutSampleRate;
+            buffer->meta_data()->setInt64(kKeyTime, timeUs);
     }
 
     *out = buffer;
@@ -300,11 +297,27 @@
 }
 
 status_t AudioSource::dataCallback(const AudioRecord::Buffer& audioBuffer) {
-    int64_t timeUs = systemTime() / 1000ll;
-    // Estimate the real sampling time of the 1st sample in this buffer
-    // from AudioRecord's latency. (Apply this adjustment first so that
-    // the start time logic is not affected.)
-    timeUs -= mRecord->latency() * 1000LL;
+    int64_t timeUs, position, timeNs;
+    ExtendedTimestamp ts;
+    ExtendedTimestamp::Location location;
+    const int32_t usPerSec = 1000000;
+
+    if (mRecord->getTimestamp(&ts) == OK &&
+            ts.getBestTimestamp(&position, &timeNs, ExtendedTimestamp::TIMEBASE_MONOTONIC,
+            &location) == OK) {
+        // Use audio timestamp.
+        timeUs = timeNs / 1000 -
+                (position - mNumFramesSkipped -
+                mNumFramesReceived + mNumFramesLost) * usPerSec / mSampleRate;
+    } else {
+        // This should not happen in normal case.
+        ALOGW("Failed to get audio timestamp, fallback to use systemclock");
+        timeUs = systemTime() / 1000ll;
+        // Estimate the real sampling time of the 1st sample in this buffer
+        // from AudioRecord's latency. (Apply this adjustment first so that
+        // the start time logic is not affected.)
+        timeUs -= mRecord->latency() * 1000LL;
+    }
 
     ALOGV("dataCallbackTimestamp: %" PRId64 " us", timeUs);
     Mutex::Autolock autoLock(mLock);
@@ -313,10 +326,15 @@
         return OK;
     }
 
+    const size_t bufferSize = audioBuffer.size;
+
     // Drop retrieved and previously lost audio data.
     if (mNumFramesReceived == 0 && timeUs < mStartTimeUs) {
         (void) mRecord->getInputFramesLost();
-        ALOGV("Drop audio data at %" PRId64 "/%" PRId64 " us", timeUs, mStartTimeUs);
+        int64_t receievedFrames = bufferSize / mRecord->frameSize();
+        ALOGV("Drop audio data(%" PRId64 " frames) at %" PRId64 "/%" PRId64 " us",
+                receievedFrames, timeUs, mStartTimeUs);
+        mNumFramesSkipped += receievedFrames;
         return OK;
     }
 
@@ -325,11 +343,7 @@
         // Initial delay
         if (mStartTimeUs > 0) {
             mStartTimeUs = timeUs - mStartTimeUs;
-        } else {
-            // Assume latency is constant.
-            mStartTimeUs += mRecord->latency() * 1000;
         }
-
         mPrevSampleTimeUs = mStartTimeUs;
     }
 
@@ -359,6 +373,7 @@
         MediaBuffer *lostAudioBuffer = new MediaBuffer(bufferSize);
         memset(lostAudioBuffer->data(), 0, bufferSize);
         lostAudioBuffer->set_range(0, bufferSize);
+        mNumFramesLost += bufferSize / mRecord->frameSize();
         queueInputBuffer_l(lostAudioBuffer, timeUs);
     }
 
@@ -367,7 +382,6 @@
         return OK;
     }
 
-    const size_t bufferSize = audioBuffer.size;
     MediaBuffer *buffer = new MediaBuffer(bufferSize);
     memcpy((uint8_t *) buffer->data(),
             audioBuffer.i16, audioBuffer.size);
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index 4ed7911..81fe0fe 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -34,7 +34,11 @@
       mMemory(mem) {
 }
 
-SecureBuffer::SecureBuffer(const sp<AMessage> &format, void *ptr, size_t size)
+sp<MediaCodecBuffer> SharedMemoryBuffer::clone(const sp<AMessage> &format) {
+    return new SharedMemoryBuffer(format, mMemory);
+}
+
+SecureBuffer::SecureBuffer(const sp<AMessage> &format, const void *ptr, size_t size)
     : MediaCodecBuffer(format, new ABuffer(nullptr, size)),
       mPointer(ptr) {
 }
@@ -46,6 +50,12 @@
       mHandle(handle) {
 }
 
+sp<MediaCodecBuffer> SecureBuffer::clone(const sp<AMessage> &format) {
+    return (mHandle == nullptr)
+            ? new SecureBuffer(format, mPointer, capacity())
+            : new SecureBuffer(format, mHandle, capacity());
+}
+
 void *SecureBuffer::getDestinationPointer() {
     return (void *)(mHandle == nullptr ? mPointer : mHandle->handle());
 }
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 408ad7a..990d4b7 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -111,6 +111,11 @@
 }
 
 static int32_t getColorFormat(const char* colorFormat) {
+    if (!colorFormat) {
+        ALOGE("Invalid color format");
+        return -1;
+    }
+
     if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) {
        return OMX_COLOR_FormatYUV420Planar;
     }
diff --git a/media/libstagefright/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp
index 1af7dc1..1b88e5d 100644
--- a/media/libstagefright/FLACExtractor.cpp
+++ b/media/libstagefright/FLACExtractor.cpp
@@ -73,6 +73,10 @@
 class FLACParser : public RefBase {
 
 public:
+    enum {
+        kMaxChannels = 8,
+    };
+
     explicit FLACParser(
         const sp<DataSource> &dataSource,
         // If metadata pointers aren't provided, we don't fill them
@@ -122,7 +126,7 @@
     // media buffers
     size_t mMaxBufferSize;
     MediaBufferGroup *mGroup;
-    void (*mCopy)(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels);
+    void (*mCopy)(short *dst, const int * src[kMaxChannels], unsigned nSamples, unsigned nChannels);
 
     // handle to underlying libFLAC parser
     FLAC__StreamDecoder *mDecoder;
@@ -139,7 +143,7 @@
     bool mWriteRequested;
     bool mWriteCompleted;
     FLAC__FrameHeader mWriteHeader;
-    const FLAC__int32 * const *mWriteBuffer;
+    FLAC__int32 const * mWriteBuffer[kMaxChannels];
 
     // most recent error reported by libFLAC parser
     FLAC__StreamDecoderErrorStatus mErrorStatus;
@@ -323,7 +327,7 @@
         mWriteRequested = false;
         // FLAC parser doesn't free or realloc buffer until next frame or finish
         mWriteHeader = frame->header;
-        mWriteBuffer = buffer;
+        memmove(mWriteBuffer, buffer, sizeof(const FLAC__int32 * const) * getChannels());
         mWriteCompleted = true;
         return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
     } else {
@@ -382,7 +386,7 @@
 
 static void copyMono8(
         short *dst,
-        const int *const *src,
+        const int * src[FLACParser::kMaxChannels],
         unsigned nSamples,
         unsigned /* nChannels */) {
     for (unsigned i = 0; i < nSamples; ++i) {
@@ -392,7 +396,7 @@
 
 static void copyStereo8(
         short *dst,
-        const int *const *src,
+        const int * src[FLACParser::kMaxChannels],
         unsigned nSamples,
         unsigned /* nChannels */) {
     for (unsigned i = 0; i < nSamples; ++i) {
@@ -401,7 +405,7 @@
     }
 }
 
-static void copyMultiCh8(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
+static void copyMultiCh8(short *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
 {
     for (unsigned i = 0; i < nSamples; ++i) {
         for (unsigned c = 0; c < nChannels; ++c) {
@@ -412,7 +416,7 @@
 
 static void copyMono16(
         short *dst,
-        const int *const *src,
+        const int * src[FLACParser::kMaxChannels],
         unsigned nSamples,
         unsigned /* nChannels */) {
     for (unsigned i = 0; i < nSamples; ++i) {
@@ -422,7 +426,7 @@
 
 static void copyStereo16(
         short *dst,
-        const int *const *src,
+        const int * src[FLACParser::kMaxChannels],
         unsigned nSamples,
         unsigned /* nChannels */) {
     for (unsigned i = 0; i < nSamples; ++i) {
@@ -431,7 +435,7 @@
     }
 }
 
-static void copyMultiCh16(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
+static void copyMultiCh16(short *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
 {
     for (unsigned i = 0; i < nSamples; ++i) {
         for (unsigned c = 0; c < nChannels; ++c) {
@@ -444,7 +448,7 @@
 
 static void copyMono24(
         short *dst,
-        const int *const *src,
+        const int * src[FLACParser::kMaxChannels],
         unsigned nSamples,
         unsigned /* nChannels */) {
     for (unsigned i = 0; i < nSamples; ++i) {
@@ -454,7 +458,7 @@
 
 static void copyStereo24(
         short *dst,
-        const int *const *src,
+        const int * src[FLACParser::kMaxChannels],
         unsigned nSamples,
         unsigned /* nChannels */) {
     for (unsigned i = 0; i < nSamples; ++i) {
@@ -463,7 +467,7 @@
     }
 }
 
-static void copyMultiCh24(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels)
+static void copyMultiCh24(short *dst, const int * src[FLACParser::kMaxChannels], unsigned nSamples, unsigned nChannels)
 {
     for (unsigned i = 0; i < nSamples; ++i) {
         for (unsigned c = 0; c < nChannels; ++c) {
@@ -474,7 +478,7 @@
 
 static void copyTrespass(
         short * /* dst */,
-        const int *const * /* src */,
+        const int *[FLACParser::kMaxChannels] /* src */,
         unsigned /* nSamples */,
         unsigned /* nChannels */) {
     TRESPASS();
@@ -499,7 +503,6 @@
       mStreamInfoValid(false),
       mWriteRequested(false),
       mWriteCompleted(false),
-      mWriteBuffer(NULL),
       mErrorStatus((FLAC__StreamDecoderErrorStatus) -1)
 {
     ALOGV("FLACParser::FLACParser");
@@ -555,7 +558,7 @@
     }
     if (mStreamInfoValid) {
         // check channel count
-        if (getChannels() == 0 || getChannels() > 8) {
+        if (getChannels() == 0 || getChannels() > kMaxChannels) {
             ALOGE("unsupported channel count %u", getChannels());
             return NO_INIT;
         }
@@ -591,7 +594,7 @@
         static const struct {
             unsigned mChannels;
             unsigned mBitsPerSample;
-            void (*mCopy)(short *dst, const int *const *src, unsigned nSamples, unsigned nChannels);
+            void (*mCopy)(short *dst, const int * src[kMaxChannels], unsigned nSamples, unsigned nChannels);
         } table[] = {
             { 1,  8, copyMono8    },
             { 2,  8, copyStereo8  },
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 5ab5f3e..e617b24 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -173,8 +173,9 @@
 
 // static
 sp<MediaCodec> MediaCodec::CreateByType(
-        const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid) {
-    sp<MediaCodec> codec = new MediaCodec(looper, pid);
+        const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
+        uid_t uid) {
+    sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);
 
     const status_t ret = codec->init(mime, true /* nameIsType */, encoder);
     if (err != NULL) {
@@ -185,8 +186,8 @@
 
 // static
 sp<MediaCodec> MediaCodec::CreateByComponentName(
-        const sp<ALooper> &looper, const AString &name, status_t *err, pid_t pid) {
-    sp<MediaCodec> codec = new MediaCodec(looper, pid);
+        const sp<ALooper> &looper, const AString &name, status_t *err, pid_t pid, uid_t uid) {
+    sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);
 
     const status_t ret = codec->init(name, false /* nameIsType */, false /* encoder */);
     if (err != NULL) {
@@ -234,7 +235,7 @@
     return new PersistentSurface(bufferProducer, bufferSource);
 }
 
-MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid)
+MediaCodec::MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid)
     : mState(UNINITIALIZED),
       mReleasedByResourceManager(false),
       mLooper(looper),
@@ -256,6 +257,11 @@
       mDequeueOutputReplyID(0),
       mHaveInputSurface(false),
       mHavePendingInputBuffers(false) {
+    if (uid == kNoUid) {
+        mUid = IPCThreadState::self()->getCallingUid();
+    } else {
+        mUid = uid;
+    }
 }
 
 MediaCodec::~MediaCodec() {
@@ -929,9 +935,8 @@
         return INVALID_OPERATION;
     }
 
-    // by the time buffers array is initialized, crypto is set
     *buffer = info.mData;
-    *format = info.mFormat;
+    *format = info.mData->format();
 
     return OK;
 }
@@ -1323,6 +1328,7 @@
                             || portIndex == kPortIndexOutput);
 
                     mPortBuffers[portIndex].clear();
+                    mPortBufferArrays[portIndex].clear();
 
                     Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
 
@@ -1349,16 +1355,14 @@
                         BufferInfo info;
                         info.mBufferID = portDesc->bufferIDAt(i);
                         info.mOwnedByClient = false;
-                        info.mData = portDesc->bufferAt(i);
-
+                        sp<MediaCodecBuffer> buffer = portDesc->bufferAt(i);
                         if (portIndex == kPortIndexInput && mCrypto != NULL) {
-                            sp<IMemory> mem = mDealer->allocate(info.mData->capacity());
-                            info.mSecureData = info.mData;
-                            info.mData = new SharedMemoryBuffer(mInputFormat, mem);
-                            info.mSharedEncryptedBuffer = mem;
+                            info.mSharedEncryptedBuffer = mDealer->allocate(buffer->capacity());
+                            buffer = new SharedMemoryBuffer(
+                                    mInputFormat, info.mSharedEncryptedBuffer);
                         }
-
                         buffers->push_back(info);
+                        mPortBufferArrays[portIndex].push_back(buffer);
                     }
 
                     if (portIndex == kPortIndexOutput) {
@@ -1382,62 +1386,6 @@
                     break;
                 }
 
-                case CodecBase::kWhatOutputFormatChanged:
-                {
-                    CHECK(msg->findMessage("format", &mOutputFormat));
-
-                    ALOGV("[%s] output format changed to: %s",
-                            mComponentName.c_str(), mOutputFormat->debugString(4).c_str());
-
-                    if (mSoftRenderer == NULL &&
-                            mSurface != NULL &&
-                            (mFlags & kFlagUsesSoftwareRenderer)) {
-                        AString mime;
-                        CHECK(mOutputFormat->findString("mime", &mime));
-
-                        // TODO: propagate color aspects to software renderer to allow better
-                        // color conversion to RGB. For now, just mark dataspace for YUV
-                        // rendering.
-                        int32_t dataSpace;
-                        if (mOutputFormat->findInt32("android._dataspace", &dataSpace)) {
-                            ALOGD("[%s] setting dataspace on output surface to #%x",
-                                    mComponentName.c_str(), dataSpace);
-                            int err = native_window_set_buffers_data_space(
-                                    mSurface.get(), (android_dataspace)dataSpace);
-                            ALOGW_IF(err != 0, "failed to set dataspace on surface (%d)", err);
-                        }
-
-                        if (mime.startsWithIgnoreCase("video/")) {
-                            mSoftRenderer = new SoftwareRenderer(mSurface, mRotationDegrees);
-                        }
-                    }
-
-                    if (mFlags & kFlagIsEncoder) {
-                        // Before we announce the format change we should
-                        // collect codec specific data and amend the output
-                        // format as necessary.
-                        mFlags |= kFlagGatherCodecSpecificData;
-                    } else if (mFlags & kFlagIsAsync) {
-                        onOutputFormatChanged();
-                    } else {
-                        mFlags |= kFlagOutputFormatChanged;
-                        postActivityNotificationIfPossible();
-                    }
-
-                    // Notify mCrypto of video resolution changes
-                    if (mCrypto != NULL) {
-                        int32_t left, top, right, bottom, width, height;
-                        if (mOutputFormat->findRect("crop", &left, &top, &right, &bottom)) {
-                            mCrypto->notifyResolution(right - left + 1, bottom - top + 1);
-                        } else if (mOutputFormat->findInt32("width", &width)
-                                && mOutputFormat->findInt32("height", &height)) {
-                            mCrypto->notifyResolution(width, height);
-                        }
-                    }
-
-                    break;
-                }
-
                 case CodecBase::kWhatOutputFramesRendered:
                 {
                     // ignore these in all states except running, and check that we have a
@@ -1528,27 +1476,65 @@
                     CHECK(msg->findInt32("flags", &omxFlags));
 
                     buffer->meta()->setInt32("omxFlags", omxFlags);
+                    if (mOutputFormat != buffer->format()) {
+                        mOutputFormat = buffer->format();
+                        ALOGV("[%s] output format changed to: %s",
+                                mComponentName.c_str(), mOutputFormat->debugString(4).c_str());
 
-                    if (mFlags & kFlagGatherCodecSpecificData) {
-                        // This is the very first output buffer after a
-                        // format change was signalled, it'll either contain
-                        // the one piece of codec specific data we can expect
-                        // or there won't be codec specific data.
-                        if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
-                            status_t err =
-                                amendOutputFormatWithCodecSpecificData(buffer);
+                        if (mSoftRenderer == NULL &&
+                                mSurface != NULL &&
+                                (mFlags & kFlagUsesSoftwareRenderer)) {
+                            AString mime;
+                            CHECK(mOutputFormat->findString("mime", &mime));
 
-                            if (err != OK) {
-                                ALOGE("Codec spit out malformed codec "
-                                      "specific data!");
+                            // TODO: propagate color aspects to software renderer to allow better
+                            // color conversion to RGB. For now, just mark dataspace for YUV
+                            // rendering.
+                            int32_t dataSpace;
+                            if (mOutputFormat->findInt32("android._dataspace", &dataSpace)) {
+                                ALOGD("[%s] setting dataspace on output surface to #%x",
+                                        mComponentName.c_str(), dataSpace);
+                                int err = native_window_set_buffers_data_space(
+                                        mSurface.get(), (android_dataspace)dataSpace);
+                                ALOGW_IF(err != 0, "failed to set dataspace on surface (%d)", err);
+                            }
+
+                            if (mime.startsWithIgnoreCase("video/")) {
+                                mSoftRenderer = new SoftwareRenderer(mSurface, mRotationDegrees);
                             }
                         }
 
-                        mFlags &= ~kFlagGatherCodecSpecificData;
+                        if (mFlags & kFlagIsEncoder) {
+                            // Before we announce the format change we should
+                            // collect codec specific data and amend the output
+                            // format as necessary.
+                            if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+                                status_t err =
+                                    amendOutputFormatWithCodecSpecificData(buffer);
+
+                                if (err != OK) {
+                                    ALOGE("Codec spit out malformed codec "
+                                          "specific data!");
+                                }
+                            }
+                        }
+
                         if (mFlags & kFlagIsAsync) {
                             onOutputFormatChanged();
                         } else {
                             mFlags |= kFlagOutputFormatChanged;
+                            postActivityNotificationIfPossible();
+                        }
+
+                        // Notify mCrypto of video resolution changes
+                        if (mCrypto != NULL) {
+                            int32_t left, top, right, bottom, width, height;
+                            if (mOutputFormat->findRect("crop", &left, &top, &right, &bottom)) {
+                                mCrypto->notifyResolution(right - left + 1, bottom - top + 1);
+                            } else if (mOutputFormat->findInt32("width", &width)
+                                    && mOutputFormat->findInt32("height", &height)) {
+                                mCrypto->notifyResolution(width, height);
+                            }
                         }
                     }
 
@@ -2170,12 +2156,10 @@
             // createInputSurface(), or persistent set by setInputSurface()),
             // give the client an empty input buffers array.
             if (portIndex != kPortIndexInput || !mHaveInputSurface) {
-                const Vector<BufferInfo> &srcBuffers = mPortBuffers[portIndex];
+                const Vector<sp<MediaCodecBuffer>> &srcBuffers = mPortBufferArrays[portIndex];
 
                 for (size_t i = 0; i < srcBuffers.size(); ++i) {
-                    const BufferInfo &info = srcBuffers.itemAt(i);
-
-                    dstBuffers->push_back(info.mData);
+                    dstBuffers->push_back(srcBuffers[i]);
                 }
             }
 
@@ -2345,7 +2329,6 @@
         mFlags &= ~kFlagOutputBuffersChanged;
         mFlags &= ~kFlagStickyError;
         mFlags &= ~kFlagIsEncoder;
-        mFlags &= ~kFlagGatherCodecSpecificData;
         mFlags &= ~kFlagIsAsync;
         mStickyError = OK;
 
@@ -2387,12 +2370,15 @@
         if (info->mNotify != NULL) {
             sp<AMessage> msg = info->mNotify;
             info->mNotify = NULL;
+            msg->setObject("buffer", (portIndex == kPortIndexInput && mCrypto != NULL)
+                    ? info->mSecureData : info->mData);
             if (isReclaim && info->mOwnedByClient) {
                 ALOGD("port %d buffer %zu still owned by client when codec is reclaimed",
                         portIndex, i);
             } else {
-                // TODO: clear memory reference.
                 info->mOwnedByClient = false;
+                info->mData.clear();
+                info->mSecureData.clear();
             }
 
             if (portIndex == kPortIndexInput) {
@@ -2404,6 +2390,7 @@
     }
 
     mAvailPortBuffers[portIndex].clear();
+    mPortBufferArrays[portIndex].clear();
 }
 
 size_t MediaCodec::updateBuffers(
@@ -2412,6 +2399,9 @@
 
     uint32_t bufferID;
     CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
+    sp<RefBase> obj;
+    CHECK(msg->findObject("buffer", &obj));
+    sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
 
     Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
 
@@ -2422,8 +2412,13 @@
             CHECK(info->mNotify == NULL);
             CHECK(msg->findMessage("reply", &info->mNotify));
 
-            info->mFormat =
-                (portIndex == kPortIndexInput) ? mInputFormat : mOutputFormat;
+            if (portIndex == kPortIndexInput && mCrypto != NULL) {
+                info->mSecureData = buffer;
+                info->mData = new SharedMemoryBuffer(
+                        buffer->format(), info->mSharedEncryptedBuffer);
+            } else {
+                info->mData = buffer;
+            }
             mAvailPortBuffers[portIndex].push_back(i);
 
             return i;
@@ -2556,13 +2551,14 @@
     if (flags & BUFFER_FLAG_CODECCONFIG) {
         buffer->meta()->setInt32("csd", true);
     }
-    // TODO: release buffer reference.
 
     // synchronization boundary for getBufferAndFormat
     {
         Mutex::Autolock al(mBufferLock);
         info->mOwnedByClient = false;
     }
+    info->mData.clear();
+    info->mSecureData.clear();
     reply->setObject("buffer", buffer);
     reply->post();
 
@@ -2634,7 +2630,7 @@
         if (mSoftRenderer != NULL) {
             std::list<FrameRenderTracker::Info> doneFrames = mSoftRenderer->render(
                     info->mData->data(), info->mData->size(),
-                    mediaTimeUs, renderTimeNs, NULL, info->mFormat);
+                    mediaTimeUs, renderTimeNs, NULL, info->mData->format());
 
             // if we are running, notify rendered frames
             if (!doneFrames.empty() && mState == STARTED && mOnFrameRenderedNotification != NULL) {
@@ -2649,7 +2645,7 @@
     }
 
     info->mNotify->setObject("buffer", info->mData);
-    // TODO: release buffer reference.
+    info->mData.clear();
     info->mNotify->post();
     info->mNotify.clear();
 
@@ -2675,13 +2671,13 @@
         info->mOwnedByClient = true;
 
         // set image-data
-        if (info->mFormat != NULL) {
+        if (info->mData->format() != NULL) {
             sp<ABuffer> imageData;
-            if (info->mFormat->findBuffer("image-data", &imageData)) {
+            if (info->mData->format()->findBuffer("image-data", &imageData)) {
                 info->mData->meta()->setBuffer("image-data", imageData);
             }
             int32_t left, top, right, bottom;
-            if (info->mFormat->findRect("crop", &left, &top, &right, &bottom)) {
+            if (info->mData->format()->findRect("crop", &left, &top, &right, &bottom)) {
                 info->mData->meta()->setRect("crop-rect", left, top, right, bottom);
             }
         }
@@ -2921,10 +2917,10 @@
     }
 
     if (mState == CONFIGURED && !mBatteryStatNotified) {
-        BatteryNotifier::getInstance().noteStartVideo();
+        BatteryNotifier::getInstance().noteStartVideo(mUid);
         mBatteryStatNotified = true;
     } else if (mState == UNINITIALIZED && mBatteryStatNotified) {
-        BatteryNotifier::getInstance().noteStopVideo();
+        BatteryNotifier::getInstance().noteStopVideo(mUid);
         mBatteryStatNotified = false;
     }
 }
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 93643c2..d3b34b7 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -1169,7 +1169,7 @@
         }
     }
 
-    if (flags & kPreferSoftwareCodecs) {
+    if (flags & kPreferSoftwareCodecs || property_get_bool("debug.stagefright.swcodec", false)) {
         matches->sort(compareSoftwareCodecsFirst);
     }
 }
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index ad27856..e3270ed 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -51,7 +51,8 @@
     for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
         TrackInfo *info = &mSelectedTracks.editItemAt(i);
 
-        CHECK_EQ((status_t)OK, info->mSource->stop());
+        status_t err = info->mSource->stop();
+        ALOGE_IF(err != OK, "error %d stopping track %zu", err, i);
     }
 
     mSelectedTracks.clear();
diff --git a/media/libstagefright/codec2/Android.mk b/media/libstagefright/codec2/Android.mk
new file mode 100644
index 0000000..a463205
--- /dev/null
+++ b/media/libstagefright/codec2/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+        C2.cpp    \
+
+LOCAL_C_INCLUDES += \
+        $(TOP)/frameworks/av/media/libstagefright/codec2/include \
+        $(TOP)/frameworks/native/include/media/hardware \
+
+LOCAL_MODULE:= libstagefright_codec2
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CLANG := true
+LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow
+
+include $(BUILD_SHARED_LIBRARY)
+
+################################################################################
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libstagefright/codec2/C2.cpp b/media/libstagefright/codec2/C2.cpp
new file mode 100644
index 0000000..a51b073
--- /dev/null
+++ b/media/libstagefright/codec2/C2.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <C2.h>
+#include <C2Buffer.h>
+#include <C2Component.h>
+#include <C2Config.h>
+#include <C2Param.h>
+#include <C2ParamDef.h>
+#include <C2Work.h>
+
+namespace android {
+
+/**
+ * There is nothing here yet. This library is built to see what symbols and methods get
+ * defined as part of the API include files.
+ *
+ * Going forward, the Codec2 library will contain utility methods that are useful for
+ * Codec2 clients.
+ */
+
+} // namespace android
+
diff --git a/media/libstagefright/codec2/include/C2.h b/media/libstagefright/codec2/include/C2.h
new file mode 100644
index 0000000..7d00a03
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2_H_
+#define C2_H_
+
+#include <string>
+#include <vector>
+#include <list>
+
+#ifdef __ANDROID__
+
+#include <utils/Errors.h>       // for status_t
+#include <utils/Timers.h>       // for nsecs_t
+
+namespace android {
+
+#else
+
+#include <errno.h>
+typedef int64_t nsecs_t;
+
+enum {
+    GRALLOC_USAGE_SW_READ_OFTEN,
+    GRALLOC_USAGE_RENDERSCRIPT,
+    GRALLOC_USAGE_HW_TEXTURE,
+    GRALLOC_USAGE_HW_COMPOSER,
+    GRALLOC_USAGE_HW_VIDEO_ENCODER,
+    GRALLOC_USAGE_PROTECTED,
+    GRALLOC_USAGE_SW_WRITE_OFTEN,
+    GRALLOC_USAGE_HW_RENDER,
+};
+
+#endif
+
+/** \mainpage Codec2
+ *
+ * Codec2 is a frame-based data processing API used by android.
+ *
+ * The framework accesses components via the \ref API.
+ */
+
+/** \ingroup API
+ *
+ * The Codec2 API defines the operation of data processing components and their interaction with
+ * the rest of the system.
+ *
+ * Coding Conventions
+ *
+ * Mitigating Binary Compatibility.
+ *
+ * While full binary compatibility is not a goal of the API (due to our use of STL), we try to
+ * mitigate binary breaks by adhering to the following conventions:
+ *
+ * - at most one vtable with placeholder virtual methods
+ * - all optional/placeholder virtual methods returning a status_t, with C2_NOT_IMPLEMENTED not
+ *   requiring any update to input/output arguments.
+ * - limiting symbol export of inline methods
+ * - use of pimpl (or shared-pimpl)
+ *
+ * Naming
+ *
+ * - all classes and types prefix with C2
+ * - classes for internal use prefix with _C2
+ * - enum values in global namespace prefix with C2_ all caps
+ * - enum values inside classes have no C2_ prefix as class already has it
+ * - supporting two kinds of enum naming: all-caps and kCamelCase
+ * \todo revisit kCamelCase for param-type
+ *
+ * Aspects
+ *
+ * Aspects define certain common behavior across a group of objects.
+ * - classes whose name matches _C2.*Aspect
+ * - only protected constructors
+ * - no desctructor and copiable
+ * - all methods are inline or static (this is opposite of the interface paradigm where all methods
+ *   are virtual, which would not work due to the at most one vtable rule.)
+ * - only private variables (this prevents subclasses interfering with the aspects.)
+ */
+
+/// \defgroup types Common Types
+/// @{
+
+/**
+ * C2String: basic string implementation
+ */
+typedef std::string C2String;
+typedef const char *C2StringLiteral;
+
+/**
+ * C2Error: status codes used.
+ */
+typedef int32_t C2Error;
+enum {
+#ifndef __ANDROID__
+    OK                  = 0,
+    BAD_VALUE           = -EINVAL,
+    BAD_INDEX           = -EOVERFLOW,
+    UNKNOWN_TRANSACTION = -EBADMSG,
+    ALREADY_EXISTS      = -EEXIST,
+    NAME_NOT_FOUND      = -ENOENT,
+    INVALID_OPERATION   = -ENOSYS,
+    NO_MEMORY           = -ENOMEM,
+    PERMISSION_DENIED   = -EPERM,
+    TIMED_OUT           = -ETIMEDOUT,
+    UNKNOWN_ERROR       = -EINVAL,
+#endif
+
+    C2_OK               = OK,                   ///< operation completed successfully
+
+    // bad input
+    C2_BAD_VALUE        = BAD_VALUE,            ///< argument has invalid value (user error)
+    C2_BAD_INDEX        = BAD_INDEX,            ///< argument uses invalid index (user error)
+    C2_UNSUPPORTED      = UNKNOWN_TRANSACTION,  ///< argument/index is value but not supported \todo is this really BAD_INDEX/VALUE?
+
+    // bad sequencing of events
+    C2_DUPLICATE        = ALREADY_EXISTS,       ///< object already exists
+    C2_NOT_FOUND        = NAME_NOT_FOUND,       ///< object not found
+    C2_BAD_STATE        = INVALID_OPERATION,    ///< operation is not permitted in the current state
+
+    // bad environment
+    C2_NO_MEMORY        = NO_MEMORY,            ///< not enough memory to complete operation
+    C2_NO_PERMISSION    = PERMISSION_DENIED,    ///< missing permission to complete operation
+    C2_TIMED_OUT        = TIMED_OUT,            ///< operation did not complete within timeout
+
+    // bad versioning
+    C2_NOT_IMPLEMENTED  = UNKNOWN_TRANSACTION,  ///< operation is not implemented (optional only) \todo for now reuse error code
+
+    // unknown fatal
+    C2_CORRUPTED        = UNKNOWN_ERROR,        ///< some unexpected error prevented the operation
+};
+
+/// @}
+
+/// \defgroup utils Utilities
+/// @{
+
+#define C2_DO_NOT_COPY(type, args...) \
+    type args& operator=(const type args&) = delete; \
+    type(const type args&) = delete; \
+
+#define C2_PURE __attribute__((pure))
+#define C2_CONST __attribute__((const))
+#define C2_HIDE __attribute__((visibility("hidden")))
+#define C2_INTERNAL __attribute__((internal_linkage))
+
+#define DEFINE_OTHER_COMPARISON_OPERATORS(type) \
+    inline bool operator!=(const type &other) { return !(*this == other); } \
+    inline bool operator<=(const type &other) { return (*this == other) || (*this < other); } \
+    inline bool operator>=(const type &other) { return !(*this < other); } \
+    inline bool operator>(const type &other) { return !(*this < other) && !(*this == other); }
+
+#define DEFINE_FIELD_BASED_COMPARISON_OPERATORS(type, field) \
+    inline bool operator<(const type &other) const { return field < other.field; } \
+    inline bool operator==(const type &other) const { return field == other.field; } \
+    DEFINE_OTHER_COMPARISON_OPERATORS(type)
+
+/// \cond INTERNAL
+
+/// \defgroup utils_internal
+/// @{
+
+template<typename... T> struct c2_types;
+
+/** specialization for a single type */
+template<typename T>
+struct c2_types<T> {
+    typedef typename std::decay<T>::type wide_type;
+    typedef wide_type narrow_type;
+    typedef wide_type mintype;
+};
+
+/** specialization for two types */
+template<typename T, typename U>
+struct c2_types<T, U> {
+    static_assert(std::is_floating_point<T>::value == std::is_floating_point<U>::value,
+                  "mixing floating point and non-floating point types is disallowed");
+    static_assert(std::is_signed<T>::value == std::is_signed<U>::value,
+                  "mixing signed and unsigned types is disallowed");
+
+    typedef typename std::decay<
+            decltype(true ? std::declval<T>() : std::declval<U>())>::type wide_type;
+    typedef typename std::decay<
+            typename std::conditional<sizeof(T) < sizeof(U), T, U>::type>::type narrow_type;
+    typedef typename std::conditional<
+            std::is_signed<T>::value, wide_type, narrow_type>::type mintype;
+};
+
+/// @}
+
+/// \endcond
+
+/**
+ * Type support utility class. Only supports similar classes, such as:
+ * - all floating point
+ * - all unsigned/all signed
+ * - all pointer
+ */
+template<typename T, typename U, typename... V>
+struct c2_types<T, U, V...> {
+    /** Common type that accommodates all template parameter types. */
+    typedef typename c2_types<typename c2_types<T, U>::wide_type, V...>::wide_type wide_type;
+    /** Narrowest type of the template parameter types. */
+    typedef typename c2_types<typename c2_types<T, U>::narrow_type, V...>::narrow_type narrow_type;
+    /** Type that accommodates the minimum value for any input for the template parameter types. */
+    typedef typename c2_types<typename c2_types<T, U>::mintype, V...>::mintype mintype;
+};
+
+/**
+ *  \ingroup utils_internal
+ * specialization for two values */
+template<typename T, typename U>
+inline constexpr typename c2_types<T, U>::wide_type c2_max(const T a, const U b) {
+    typedef typename c2_types<T, U>::wide_type wide_type;
+    return ({ wide_type a_(a), b_(b); a_ > b_ ? a_ : b_; });
+}
+
+/**
+ * Finds the maximum value of a list of "similarly typed" values.
+ *
+ * This is an extension to std::max where the types do not have to be identical, and the smallest
+ * resulting type is used that accommodates the argument types.
+ *
+ * \note Value types must be similar, e.g. all floating point, all pointers, all signed, or all
+ * unsigned.
+ *
+ * @return the largest of the input arguments.
+ */
+template<typename T, typename U, typename... V>
+constexpr typename c2_types<T, U, V...>::wide_type c2_max(const T a, const U b, const V ... c) {
+    typedef typename c2_types<T, U, V...>::wide_type wide_type;
+    return ({ wide_type a_(a), b_(c2_max(b, c...)); a_ > b_ ? a_ : b_; });
+}
+
+/**
+ *  \ingroup utils_internal
+ * specialization for two values */
+template<typename T, typename U>
+inline constexpr typename c2_types<T, U>::mintype c2_min(const T a, const U b) {
+    typedef typename c2_types<T, U>::wide_type wide_type;
+    return ({
+        wide_type a_(a), b_(b);
+        static_cast<typename c2_types<T, U>::mintype>(a_ < b_ ? a_ : b_);
+    });
+}
+
+/**
+ * Finds the minimum value of a list of "similarly typed" values.
+ *
+ * This is an extension to std::min where the types do not have to be identical, and the smallest
+ * resulting type is used that accommodates the argument types.
+ *
+ * \note Value types must be similar, e.g. all floating point, all pointers, all signed, or all
+ * unsigned.
+ *
+ * @return the smallest of the input arguments.
+ */
+template<typename T, typename U, typename... V>
+constexpr typename c2_types<T, U, V...>::mintype c2_min(const T a, const U b, const V ... c) {
+    typedef typename c2_types<U, V...>::mintype rest_type;
+    typedef typename c2_types<T, rest_type>::wide_type wide_type;
+    return ({
+        wide_type a_(a), b_(c2_min(b, c...));
+        static_cast<typename c2_types<T, rest_type>::mintype>(a_ < b_ ? a_ : b_);
+    });
+}
+
+/// @}
+
+#ifdef __ANDROID__
+} // namespace android
+#endif
+
+#endif  // C2_H_
diff --git a/media/libstagefright/codec2/include/C2Buffer.h b/media/libstagefright/codec2/include/C2Buffer.h
new file mode 100644
index 0000000..9f6b487
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2Buffer.h
@@ -0,0 +1,1777 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2BUFFER_H_
+#define C2BUFFER_H_
+
+#include <C2.h>
+#include <C2Param.h> // for C2Info
+
+#include <list>
+#include <memory>
+
+typedef int C2Fence;
+
+#ifdef __ANDROID__
+
+// #include <system/window.h>
+#include <cutils/native_handle.h>
+#include <hardware/gralloc.h> // TODO: remove
+
+typedef native_handle_t C2Handle;
+
+#else
+
+typedef void* C2Handle;
+
+#endif
+
+namespace android {
+
+/// \defgroup buffer Buffers
+/// @{
+
+/// \defgroup buffer_sync Synchronization
+/// @{
+
+/**
+ * Synchronization is accomplished using event and fence objects.
+ *
+ * These are cross-process extensions of promise/future infrastructure.
+ * Events are analogous to std::promise<void>, whereas fences are to std::shared_future<void>.
+ *
+ * Fences and events are shareable/copyable.
+ *
+ * Fences are used in two scenarios, and all copied instances refer to the same event.
+ * \todo do events need to be copyable or should they be unique?
+ *
+ * acquire sync fence object: signaled when it is safe for the component or client to access
+ * (the contents of) an object.
+ *
+ * release sync fence object: \todo
+ *
+ * Fences can be backed by hardware. Hardware fences are guaranteed to signal NO MATTER WHAT within
+ * a short (platform specific) amount of time; this guarantee is usually less than 15 msecs.
+ */
+
+/**
+ * Fence object used by components and the framework.
+ *
+ * Implements the waiting for an event, analogous to a 'future'.
+ *
+ * To be implemented by vendors if using HW fences.
+ */
+class C2Fence {
+public:
+    /**
+     * Waits for a fence to be signaled with a timeout.
+     *
+     * \todo a mechanism to cancel a wait - for now the only way to do this is to abandon the
+     * event, but fences are shared so canceling a wait will cancel all waits.
+     *
+     * \param timeoutNs           the maximum time to wait in nsecs
+     *
+     * \retval C2_OK            the fence has been signaled
+     * \retval C2_TIMED_OUT     the fence has not been signaled within the timeout
+     * \retval C2_BAD_STATE     the fence has been abandoned without being signaled (it will never
+     *                          be signaled)
+     * \retval C2_NO_PERMISSION no permission to wait for the fence (unexpected - system)
+     * \retval C2_CORRUPTED     some unknown error prevented waiting for the fence (unexpected)
+     */
+    C2Error wait(nsecs_t timeoutNs);
+
+    /**
+     * Used to check if this fence is valid (if there is a chance for it to be signaled.)
+     * A fence becomes invalid if the controling event is destroyed without it signaling the fence.
+     *
+     * \return whether this fence is valid
+     */
+    bool valid() const;
+
+    /**
+     * Used to check if this fence has been signaled (is ready).
+     *
+     * \return whether this fence has been signaled
+     */
+    bool ready() const;
+
+    /**
+     * Returns a file descriptor that can be used to wait for this fence in a select system call.
+     * \note The returned file descriptor, if valid, must be closed by the caller.
+     *
+     * This can be used in e.g. poll() system calls. This file becomes readable (POLLIN) when the
+     * fence is signaled, and bad (POLLERR) if the fence is abandoned.
+     *
+     * \return a file descriptor representing this fence (with ownership), or -1 if the fence
+     * has already been signaled (\todo or abandoned).
+     *
+     * \todo this must be compatible with fences used by gralloc
+     */
+    int fd() const;
+
+    /**
+     * Returns whether this fence is a hardware-backed fence.
+     * \return whether this is a hardware fence
+     */
+    bool isHW() const;
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * Event object used by components and the framework.
+ *
+ * Implements the signaling of an event, analogous to a 'promise'.
+ *
+ * Hardware backed events do not go through this object, and must be exposed directly as fences
+ * by vendors.
+ */
+class C2Event {
+public:
+    /**
+     * Returns a fence for this event.
+     */
+    C2Fence fence() const;
+
+    /**
+     * Signals (all) associated fence(s).
+     * This has no effect no effect if the event was already signaled or abandoned.
+     *
+     * \retval C2_OK            the fence(s) were successfully signaled
+     * \retval C2_BAD_STATE     the fence(s) have already been abandoned or merged (caller error)
+     * \retval C2_ALREADY_EXISTS the fence(s) have already been signaled (caller error)
+     * \retval C2_NO_PERMISSION no permission to signal the fence (unexpected - system)
+     * \retval C2_CORRUPTED     some unknown error prevented signaling the fence(s) (unexpected)
+     */
+    C2Error fire();
+
+    /**
+     * Trigger this event from the merging of the supplied fences. This means that it will be
+     * abandoned if any of these fences have been abandoned, and it will be fired if all of these
+     * fences have been signaled.
+     *
+     * \retval C2_OK            the merging was successfully done
+     * \retval C2_NO_MEMORY     not enough memory to perform the merging
+     * \retval C2_ALREADY_EXISTS    the fence have already been merged (caller error)
+     * \retval C2_BAD_STATE     the fence have already been signaled or abandoned (caller error)
+     * \retval C2_NO_PERMISSION no permission to merge the fence (unexpected - system)
+     * \retval C2_CORRUPTED     some unknown error prevented merging the fence(s) (unexpected)
+     */
+    C2Error merge(std::vector<C2Fence> fences);
+
+    /**
+     * Abandons the event and any associated fence(s).
+     * \note Call this to explicitly abandon an event before it is destructed to avoid a warning.
+     *
+     * This has no effect no effect if the event was already signaled or abandoned.
+     *
+     * \retval C2_OK            the fence(s) were successfully signaled
+     * \retval C2_BAD_STATE     the fence(s) have already been signaled or merged (caller error)
+     * \retval C2_ALREADY_EXISTS    the fence(s) have already been abandoned (caller error)
+     * \retval C2_NO_PERMISSION no permission to abandon the fence (unexpected - system)
+     * \retval C2_CORRUPTED     some unknown error prevented signaling the fence(s) (unexpected)
+     */
+    C2Error abandon();
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/// \addtogroup buf_internal Internal
+/// @{
+
+/**
+ * Interface for objects that encapsulate an updatable error value.
+ */
+struct _C2InnateError {
+    inline C2Error error() const { return mError; }
+
+protected:
+    _C2InnateError(C2Error error) : mError(error) { }
+
+    C2Error mError; // this error is updatable by the object
+};
+
+/// @}
+
+/**
+ * This is a utility template for objects protected by an acquire fence, so that errors during
+ * acquiring the object are propagated to the object itself.
+ */
+template<typename T>
+class C2Acquirable : public C2Fence {
+public:
+    /**
+     * Acquires the object protected by an acquire fence. Any errors during the mapping will be
+     * passed to the object.
+     *
+     * \return acquired object potentially invalidated if waiting for the fence failed.
+     */
+    T get();
+
+protected:
+    C2Acquirable(C2Error error, C2Fence fence, T t) : C2Fence(fence), mInitialError(error), mT(t) { }
+
+private:
+    C2Error mInitialError;
+    T mT; // TODO: move instead of copy
+};
+
+/// @}
+
+/// \defgroup linear Linear Data Blocks
+/// @{
+
+/**************************************************************************************************
+  LINEAR ASPECTS, BLOCKS AND VIEWS
+**************************************************************************************************/
+
+/**
+ * Common aspect for all objects that have a linear capacity.
+ */
+class _C2LinearCapacityAspect {
+/// \name Linear capacity interface
+/// @{
+public:
+    inline uint32_t capacity() const { return mCapacity; }
+
+protected:
+
+#if UINTPTR_MAX == 0xffffffff
+    static_assert(sizeof(size_t) == sizeof(uint32_t), "size_t is too big");
+#else
+    static_assert(sizeof(size_t) > sizeof(uint32_t), "size_t is too small");
+    // explicitly disable construction from size_t
+    inline explicit _C2LinearCapacityAspect(size_t capacity) = delete;
+#endif
+
+    inline explicit _C2LinearCapacityAspect(uint32_t capacity)
+      : mCapacity(capacity) { }
+
+    inline explicit _C2LinearCapacityAspect(const _C2LinearCapacityAspect *parent)
+        : mCapacity(parent == nullptr ? 0 : parent->capacity()) { }
+
+private:
+    const uint32_t mCapacity;
+/// @}
+};
+
+/**
+ * Aspect for objects that have a linear range.
+ *
+ * This class is copiable.
+ */
+class _C2LinearRangeAspect : public _C2LinearCapacityAspect {
+/// \name Linear range interface
+/// @{
+public:
+    inline uint32_t offset() const { return mOffset; }
+    inline uint32_t size() const { return mSize; }
+
+protected:
+    inline explicit _C2LinearRangeAspect(const _C2LinearCapacityAspect *parent)
+        : _C2LinearCapacityAspect(parent),
+          mOffset(0),
+          mSize(capacity()) { }
+
+    inline _C2LinearRangeAspect(const _C2LinearCapacityAspect *parent, size_t offset, size_t size)
+        : _C2LinearCapacityAspect(parent),
+          mOffset(c2_min(offset, capacity())),
+          mSize(c2_min(size, capacity() - mOffset)) { }
+
+    // subsection of the two [offset, offset + size] ranges
+    inline _C2LinearRangeAspect(const _C2LinearRangeAspect *parent, size_t offset, size_t size)
+        : _C2LinearCapacityAspect(parent == nullptr ? 0 : parent->capacity()),
+          mOffset(c2_min(c2_max(offset, parent == nullptr ? 0 : parent->offset()), capacity())),
+          mSize(c2_min(c2_min(size, parent == nullptr ? 0 : parent->size()), capacity() - mOffset)) { }
+
+private:
+    friend class _C2EditableLinearRange;
+    // invariants 0 <= mOffset <= mOffset + mSize <= capacity()
+    uint32_t mOffset;
+    uint32_t mSize;
+/// @}
+};
+
+/**
+ * Aspect for objects that have an editable linear range.
+ *
+ * This class is copiable.
+ */
+class _C2EditableLinearRange : public _C2LinearRangeAspect {
+protected:
+    inline explicit _C2EditableLinearRange(const _C2LinearCapacityAspect *parent)
+        : _C2LinearRangeAspect(parent) { }
+
+    inline _C2EditableLinearRange(const _C2LinearCapacityAspect *parent, size_t offset, size_t size)
+        : _C2LinearRangeAspect(parent, offset, size) { }
+
+    // subsection of the two [offset, offset + size] ranges
+    inline _C2EditableLinearRange(const _C2LinearRangeAspect *parent, size_t offset, size_t size)
+        : _C2LinearRangeAspect(parent, offset, size) { }
+
+/// \name Editable linear range interface
+/// @{
+
+    /**
+     * Sets the offset to |offset|, while trying to keep the end of the buffer unchanged (e.g.
+     * size will grow if offset is decreased, and may shrink if offset is increased.) Returns
+     * true if successful, which is equivalent to if 0 <= |offset| <= capacity().
+     *
+     * Note: setting offset and size will yield different result depending on the order of the
+     * operations. Always set offset first to ensure proper size.
+     */
+    inline bool setOffset(uint32_t offset) {
+        if (offset > capacity()) {
+            return false;
+        }
+
+        if (offset > mOffset + mSize) {
+            mSize = 0;
+        } else {
+            mSize = mOffset + mSize - offset;
+        }
+        mOffset = offset;
+        return true;
+    }
+    /**
+     * Sets the size to |size|. Returns true if successful, which is equivalent to
+     * if 0 <= |size| <= capacity() - offset().
+     *
+     * Note: setting offset and size will yield different result depending on the order of the
+     * operations. Always set offset first to ensure proper size.
+     */
+    inline bool setSize(uint32_t size) {
+        if (size > capacity() - mOffset) {
+            return false;
+        } else {
+            mSize = size;
+            return true;
+        }
+    }
+    /**
+     * Sets the offset to |offset| with best effort. Same as setOffset() except that offset will
+     * be clamped to the buffer capacity.
+     *
+     * Note: setting offset and size (even using best effort) will yield different result depending
+     * on the order of the operations. Always set offset first to ensure proper size.
+     */
+    inline void setOffset_be(uint32_t offset) {
+        if (offset > capacity()) {
+            offset = capacity();
+        }
+        if (offset > mOffset + mSize) {
+            mSize = 0;
+        } else {
+            mSize = mOffset + mSize - offset;
+        }
+        mOffset = offset;
+    }
+    /**
+     * Sets the size to |size| with best effort. Same as setSize() except that the selected region
+     * will be clamped to the buffer capacity (e.g. size is clamped to [0, capacity() - offset()]).
+     *
+     * Note: setting offset and size (even using best effort) will yield different result depending
+     * on the order of the operations. Always set offset first to ensure proper size.
+     */
+    inline void setSize_be(uint32_t size) {
+        mSize = std::min(size, capacity() - mOffset);
+    }
+/// @}
+};
+
+// ================================================================================================
+//  BLOCKS
+// ================================================================================================
+
+/**
+ * Blocks are sections of allocations. They can be either 1D or 2D.
+ */
+
+class C2LinearAllocation;
+
+class C2Block1D : public _C2LinearRangeAspect {
+public:
+    const C2Handle *handle() const;
+
+protected:
+    C2Block1D(std::shared_ptr<C2LinearAllocation> alloc);
+    C2Block1D(std::shared_ptr<C2LinearAllocation> alloc, size_t offset, size_t size);
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * Read view provides read-only access for a linear memory segment.
+ *
+ * This class is copiable.
+ */
+class C2ReadView : public _C2LinearCapacityAspect {
+public:
+    /**
+     * \return pointer to the start of the block or nullptr on error.
+     */
+    const uint8_t *data();
+
+    /**
+     * Returns a portion of this view.
+     *
+     * \param offset  the start offset of the portion. \note This is clamped to the capacity of this
+     *              view.
+     * \param size    the size of the portion. \note This is clamped to the remaining data from offset.
+     *
+     * \return a read view containing a portion of this view
+     */
+    C2ReadView subView(size_t offset, size_t size) const;
+
+    /**
+     * \return error during the creation/mapping of this view.
+     */
+    C2Error error();
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * Write view provides read/write access for a linear memory segment.
+ *
+ * This class is copiable. \todo movable only?
+ */
+class C2WriteView : public _C2EditableLinearRange {
+public:
+    /**
+     * Start of the block.
+     *
+     * \return pointer to the start of the block or nullptr on error.
+     */
+    uint8_t *base();
+
+    /**
+     * \return pointer to the block at the current offset or nullptr on error.
+     */
+    uint8_t *data();
+
+    /**
+     * \return error during the creation/mapping of this view.
+     */
+    C2Error error();
+
+private:
+    class Impl;
+    /// \todo should this be unique_ptr to make this movable only - to avoid inconsistent regions
+    /// between copies.
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * A constant (read-only) linear block (portion of an allocation) with an acquire fence.
+ * Blocks are unmapped when created, and can be mapped into a read view on demand.
+ *
+ * This class is copiable and contains a reference to the allocation that it is based on.
+ */
+class C2ConstLinearBlock : public C2Block1D {
+public:
+    /**
+     * Maps this block into memory and returns a read view for it.
+     *
+     * \return a read view for this block.
+     */
+    C2Acquirable<C2ReadView> map() const;
+
+    /**
+     * Returns a portion of this block.
+     *
+     * \param offset  the start offset of the portion. \note This is clamped to the capacity of this
+     *              block.
+     * \param size    the size of the portion. \note This is clamped to the remaining data from offset.
+     *
+     * \return a constant linear block containing a portion of this block
+     */
+    C2ConstLinearBlock subBlock(size_t offset, size_t size) const;
+
+    /**
+     * Returns the acquire fence for this block.
+     *
+     * \return a fence that must be waited on before reading the block.
+     */
+    C2Fence fence() const { return mFence; }
+
+private:
+    C2Fence mFence;
+};
+
+/**
+ * Linear block is a writeable 1D block. Once written, it can be shared in whole or in parts with
+ * consumers/readers as read-only const linear block(s).
+ */
+class C2LinearBlock : public C2Block1D {
+public:
+    /**
+     * Maps this block into memory and returns a write view for it.
+     *
+     * \return a write view for this block.
+     */
+    C2Acquirable<C2WriteView> map();
+
+    /**
+     * Creates a read-only const linear block for a portion of this block; optionally protected
+     * by an acquire fence. There are two ways to use this:
+     *
+     * 1) share ready block after writing data into the block. In this case no fence shall be
+     *    supplied, and the block shall not be modified after calling this method.
+     * 2) share block metadata before actually (finishing) writing the data into the block. In
+     *    this case a fence must be supplied that will be triggered when the data is written.
+     *    The block shall be modified only until firing the event for the fence.
+     */
+    C2ConstLinearBlock share(size_t offset, size_t size, C2Fence fence);
+};
+
+/// @}
+
+/**************************************************************************************************
+  CIRCULAR BLOCKS AND VIEWS
+**************************************************************************************************/
+
+/// \defgroup circular Circular buffer support
+/// @{
+
+/**
+ * Circular blocks can be used to share data between a writer and a reader (and/or other consumers)-
+ * in a memory-efficient way by reusing a section of memory. Circular blocks are a bit more complex
+ * than single reader/single writer schemes to facilitate block-based consuming of data.
+ *
+ * They can operate in two modes:
+ *
+ * 1) one writer that creates blocks to be consumed (this model can be used by components)
+ *
+ * 2) one writer that writes continuously, and one reader that can creates blocks to be consumed
+ *    by further recipients (this model is used by the framework, and cannot be used by components.)
+ *
+ * Circular blocks have four segments with running pointers:
+ *  - reserved: data reserved and available for the writer
+ *  - committed: data committed by the writer and available to the reader (if present)
+ *  - used: data used by consumers (if present)
+ *  - available: unused data available to be reserved
+ */
+class C2CircularBlock : public C2Block1D {
+    // TODO: add methods
+
+private:
+    size_t mReserved __unused;   // end of reserved section
+    size_t mCommitted __unused;  // end of committed section
+    size_t mUsed __unused;       // end of used section
+    size_t mFree __unused;       // end of free section
+};
+
+class _C2CircularBlockSegment : public _C2LinearCapacityAspect {
+public:
+    /**
+     * Returns the available size for this segment.
+     *
+     * \return currently available size for this segment
+     */
+    size_t available() const;
+
+    /**
+     * Reserve some space for this segment from its current start.
+     *
+     * \param size    desired space in bytes
+     * \param fence   a pointer to an acquire fence. If non-null, the reservation is asynchronous and
+     *              a fence will be stored here that will be signaled when the reservation is
+     *              complete. If null, the reservation is synchronous.
+     *
+     * \retval C2_OK            the space was successfully reserved
+     * \retval C2_NO_MEMORY     the space requested cannot be reserved
+     * \retval C2_TIMED_OUT     the reservation timed out \todo when?
+     * \retval C2_CORRUPTED     some unknown error prevented reserving space. (unexpected)
+     */
+    C2Error reserve(size_t size, C2Fence *fence /* nullable */);
+
+    /**
+     * Abandons a portion of this segment. This will move to the beginning of this segment.
+     *
+     * \note This methods is only allowed if this segment is producing blocks.
+     *
+     * \param size    number of bytes to abandon
+     *
+     * \retval C2_OK            the data was successfully abandoned
+     * \retval C2_TIMED_OUT     the operation timed out (unexpected)
+     * \retval C2_CORRUPTED     some unknown error prevented abandoning the data (unexpected)
+     */
+    C2Error abandon(size_t size);
+
+    /**
+     * Share a portion as block(s) with consumers (these are moved to the used section).
+     *
+     * \note This methods is only allowed if this segment is producing blocks.
+     * \note Share does not move the beginning of the segment. (\todo add abandon/offset?)
+     *
+     * \param size    number of bytes to share
+     * \param fence   fence to be used for the section
+     * \param blocks  list where the blocks of the section are appended to
+     *
+     * \retval C2_OK            the portion was successfully shared
+     * \retval C2_NO_MEMORY     not enough memory to share the portion
+     * \retval C2_TIMED_OUT     the operation timed out (unexpected)
+     * \retval C2_CORRUPTED     some unknown error prevented sharing the data (unexpected)
+     */
+    C2Error share(size_t size, C2Fence fence, std::list<C2ConstLinearBlock> &blocks);
+
+    /**
+     * Returns the beginning offset of this segment from the start of this circular block.
+     *
+     * @return beginning offset
+     */
+    size_t begin();
+
+    /**
+     * Returns the end offset of this segment from the start of this circular block.
+     *
+     * @return end offset
+     */
+    size_t end();
+};
+
+/**
+ * A circular write-view is a dynamic mapped view for a segment of a circular block. Care must be
+ * taken when using this view so that only the section owned by the segment is modified.
+ */
+class C2CircularWriteView : public _C2LinearCapacityAspect {
+public:
+    /**
+     * Start of the circular block.
+     * \note the segment does not own this pointer.
+     *
+     * \return pointer to the start of the circular block or nullptr on error.
+     */
+    uint8_t *base();
+
+    /**
+     * \return error during the creation/mapping of this view.
+     */
+    C2Error error();
+};
+
+/**
+ * The writer of a circular buffer.
+ *
+ * Can commit data to a reader (not supported for components) OR share data blocks directly with a
+ * consumer.
+ *
+ * If a component supports outputting data into circular buffers, it must allocate a circular
+ * block and use a circular writer.
+ */
+class C2CircularWriter : public _C2CircularBlockSegment {
+public:
+    /**
+     * Commits a portion of this segment to the next segment. This moves the beginning of the
+     * segment.
+     *
+     * \param size    number of bytes to commit to the next segment
+     * \param fence   fence used for the commit (the fence must signal before the data is committed)
+     */
+    C2Error commit(size_t size, C2Fence fence);
+
+    /**
+     * Maps this block into memory and returns a write view for it.
+     *
+     * \return a write view for this block.
+     */
+    C2Acquirable<C2CircularWriteView> map();
+};
+
+/// @}
+
+/// \defgroup graphic Graphic Data Blocks
+/// @{
+
+/**
+ * Interface for objects that have a width and height (planar capacity).
+ */
+class _C2PlanarCapacityAspect {
+/// \name Planar capacity interface
+/// @{
+public:
+    inline uint32_t width() const { return mWidth; }
+    inline uint32_t height() const { return mHeight; }
+
+protected:
+    inline _C2PlanarCapacityAspect(uint32_t width, uint32_t height)
+      : mWidth(width), mHeight(height) { }
+
+    inline _C2PlanarCapacityAspect(const _C2PlanarCapacityAspect *parent)
+        : mWidth(parent == nullptr ? 0 : parent->width()),
+          mHeight(parent == nullptr ? 0 : parent->height()) { }
+
+private:
+    const uint32_t mWidth;
+    const uint32_t mHeight;
+/// @}
+};
+
+/**
+ * C2Rect: rectangle type with non-negative coordinates.
+ *
+ * \note This struct has public fields without getters/setters. All methods are inline.
+ */
+struct C2Rect {
+// public:
+    uint32_t mLeft;
+    uint32_t mTop;
+    uint32_t mWidth;
+    uint32_t mHeight;
+
+    inline C2Rect(uint32_t width, uint32_t height)
+        : C2Rect(width, height, 0, 0) { }
+
+    inline C2Rect(uint32_t width, uint32_t height, uint32_t left, uint32_t top)
+        : mLeft(left), mTop(top), mWidth(width), mHeight(height) { }
+
+    // utility methods
+
+    inline bool isEmpty() const {
+        return mWidth == 0 || mHeight == 0;
+    }
+
+    inline bool isValid() const {
+        return mLeft <= ~mWidth && mTop <= ~mHeight;
+    }
+
+    inline operator bool() const {
+        return isValid() && !isEmpty();
+    }
+
+    inline bool operator!() const {
+        return !bool(*this);
+    }
+
+    inline bool contains(const C2Rect &other) const {
+        if (!isValid() || !other.isValid()) {
+            return false;
+        } else if (other.isEmpty()) {
+            return true;
+        } else {
+            return mLeft <= other.mLeft && mTop <= other.mTop
+                    && mLeft + mWidth >= other.mLeft + other.mWidth
+                    && mTop + mHeight >= other.mTop + other.mHeight;
+        }
+    }
+
+    inline bool operator==(const C2Rect &other) const {
+        if (!isValid()) {
+            return !other.isValid();
+        } else if (isEmpty()) {
+            return other.isEmpty();
+        } else {
+            return mLeft == other.mLeft && mTop == other.mTop
+                    && mWidth == other.mWidth && mHeight == other.mHeight;
+        }
+    }
+
+    inline bool operator!=(const C2Rect &other) const {
+        return !operator==(other);
+    }
+
+    inline bool operator>=(const C2Rect &other) const {
+        return contains(other);
+    }
+
+    inline bool operator>(const C2Rect &other) const {
+        return contains(other) && !operator==(other);
+    }
+
+    inline bool operator<=(const C2Rect &other) const {
+        return other.contains(*this);
+    }
+
+    inline bool operator<(const C2Rect &other) const {
+        return other.contains(*this) && !operator==(other);
+    }
+};
+
+/**
+ * C2PlaneInfo: information on the layout of flexible planes.
+ *
+ * Public fields without getters/setters.
+ */
+struct C2PlaneInfo {
+// public:
+    enum Channel : uint32_t {
+        Y,
+        R,
+        G,
+        B,
+        A,
+        Cr,
+        Cb,
+    } mChannel;
+
+    int32_t mColInc;               // column increment in bytes. may be negative
+    int32_t mRowInc;               // row increment in bytes. may be negative
+    uint32_t mHorizSubsampling;    // subsampling compared to width
+    uint32_t mVertSubsampling;     // subsampling compared to height
+
+    uint32_t mBitDepth;
+    uint32_t mAllocatedDepth;
+
+    inline ssize_t minOffset(uint32_t width, uint32_t height) {
+        ssize_t offs = 0;
+        if (width > 0 && mColInc < 0) {
+            offs += mColInc * (ssize_t)(width - 1);
+        }
+        if (height > 0 && mRowInc < 0) {
+            offs += mRowInc * (ssize_t)(height - 1);
+        }
+        return offs;
+    }
+
+    inline ssize_t maxOffset(uint32_t width, uint32_t height, uint32_t allocatedDepth) {
+        ssize_t offs = (allocatedDepth + 7) >> 3;
+        if (width > 0 && mColInc > 0) {
+            offs += mColInc * (ssize_t)(width - 1);
+        }
+        if (height > 0 && mRowInc > 0) {
+            offs += mRowInc * (ssize_t)(height - 1);
+        }
+        return offs;
+    }
+};
+
+struct C2PlaneLayout {
+public:
+    enum Type : uint32_t {
+        MEDIA_IMAGE_TYPE_UNKNOWN = 0,
+        MEDIA_IMAGE_TYPE_YUV = 0x100,
+        MEDIA_IMAGE_TYPE_YUVA,
+        MEDIA_IMAGE_TYPE_RGB,
+        MEDIA_IMAGE_TYPE_RGBA,
+    };
+
+    Type mType;
+    uint32_t mNumPlanes;               // number of planes
+
+    enum PlaneIndex : uint32_t {
+        Y = 0,
+        U = 1,
+        V = 2,
+        R = 0,
+        G = 1,
+        B = 2,
+        A = 3,
+        MAX_NUM_PLANES = 4,
+    };
+
+    C2PlaneInfo mPlanes[MAX_NUM_PLANES];
+};
+
+/**
+ * Aspect for objects that have a planar section (crop rectangle).
+ *
+ * This class is copiable.
+ */
+class _C2PlanarSection : public _C2PlanarCapacityAspect {
+/// \name Planar section interface
+/// @{
+public:
+    // crop can be an empty rect, does not have to line up with subsampling
+    // NOTE: we do not support floating-point crop
+    inline const C2Rect crop() { return mCrop; }
+
+    /**
+     *  Sets crop to crop intersected with [(0,0) .. (width, height)]
+     */
+    inline void setCrop_be(const C2Rect &crop);
+
+    /**
+     * If crop is within the dimensions of this object, it sets crop to it.
+     *
+     * \return true iff crop is within the dimensions of this object
+     */
+    inline bool setCrop(const C2Rect &crop);
+
+private:
+    C2Rect mCrop;
+/// @}
+};
+
+class C2Block2D : public _C2PlanarSection {
+public:
+    const C2Handle *handle() const;
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * Graphic view provides read or read-write access for a graphic block.
+ *
+ * This class is copiable.
+ *
+ * \note Due to the subsampling of graphic buffers, a read view must still contain a crop rectangle
+ * to ensure subsampling is followed. This results in nearly identical interface between read and
+ * write views, so C2GraphicView can encompass both of them.
+ */
+class C2GraphicView : public _C2PlanarSection {
+public:
+    /**
+     * \return pointer to the start of the block or nullptr on error.
+     */
+    const uint8_t *data() const;
+
+    /**
+     * \return pointer to the start of the block or nullptr on error.
+     */
+    uint8_t *data();
+
+    /**
+     * Returns a section of this view.
+     *
+     * \param rect    the dimension of the section. \note This is clamped to the crop of this view.
+     *
+     * \return a read view containing the requested section of this view
+     */
+    const C2GraphicView subView(const C2Rect &rect) const;
+    C2GraphicView subView(const C2Rect &rect);
+
+    /**
+     * \return error during the creation/mapping of this view.
+     */
+    C2Error error() const;
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+};
+
+/**
+ * A constant (read-only) graphic block (portion of an allocation) with an acquire fence.
+ * Blocks are unmapped when created, and can be mapped into a read view on demand.
+ *
+ * This class is copiable and contains a reference to the allocation that it is based on.
+ */
+class C2ConstGraphicBlock : public C2Block2D {
+public:
+    /**
+     * Maps this block into memory and returns a read view for it.
+     *
+     * \return a read view for this block.
+     */
+    C2Acquirable<const C2GraphicView> map() const;
+
+    /**
+     * Returns a section of this block.
+     *
+     * \param rect    the coordinates of the section. \note This is clamped to the crop rectangle of
+     *              this block.
+     *
+     * \return a constant graphic block containing a portion of this block
+     */
+    C2ConstGraphicBlock subBlock(const C2Rect &rect) const;
+
+    /**
+     * Returns the acquire fence for this block.
+     *
+     * \return a fence that must be waited on before reading the block.
+     */
+    C2Fence fence() const { return mFence; }
+
+private:
+    C2Fence mFence;
+};
+
+/**
+ * Graphic block is a writeable 2D block. Once written, it can be shared in whole or in part with
+ * consumers/readers as read-only const graphic block.
+ */
+class C2GraphicBlock : public C2Block2D {
+public:
+    /**
+     * Maps this block into memory and returns a write view for it.
+     *
+     * \return a write view for this block.
+     */
+    C2Acquirable<C2GraphicView> map();
+
+    /**
+     * Creates a read-only const linear block for a portion of this block; optionally protected
+     * by an acquire fence. There are two ways to use this:
+     *
+     * 1) share ready block after writing data into the block. In this case no fence shall be
+     *    supplied, and the block shall not be modified after calling this method.
+     * 2) share block metadata before actually (finishing) writing the data into the block. In
+     *    this case a fence must be supplied that will be triggered when the data is written.
+     *    The block shall be modified only until firing the event for the fence.
+     */
+    C2ConstGraphicBlock share(const C2Rect &crop, C2Fence fence);
+};
+
+/// @}
+
+/// \defgroup buffer_onj Buffer objects
+/// @{
+
+// ================================================================================================
+//  BUFFERS
+// ================================================================================================
+
+/// \todo: Do we still need this?
+///
+// There are 2 kinds of buffers: linear or graphic. Linear buffers can contain a single block, or
+// a list of blocks (LINEAR_CHUNKS). Support for list of blocks is optional, and can allow consuming
+// data from circular buffers or scattered data sources without extra memcpy. Currently, list of
+// graphic blocks is not supported.
+
+class C2LinearBuffer;   // read-write buffer
+class C2GraphicBuffer;  // read-write buffer
+class C2LinearChunksBuffer;
+
+/**
+ * C2BufferData: the main, non-meta data of a buffer. A buffer can contain either linear blocks
+ * or graphic blocks, and can contain either a single block or multiple blocks. This is determined
+ * by its type.
+ */
+class C2BufferData {
+public:
+    /**
+     *  The type of buffer data.
+     */
+    enum Type : uint32_t {
+        LINEAR,             ///< the buffer contains a single linear block
+        LINEAR_CHUNKS,      ///< the buffer contains one or more linear blocks
+        GRAPHIC,            ///< the buffer contains a single graphic block
+        GRAPHIC_CHUNKS,     ///< the buffer contains one of more graphic blocks
+    };
+
+    /**
+     * Gets the type of this buffer (data).
+     * \return the type of this buffer data.
+     */
+    Type type() const;
+
+    /**
+     * Gets the linear blocks of this buffer.
+     * \return a constant list of const linear blocks of this buffer.
+     * \retval empty list if this buffer does not contain linear block(s).
+     */
+    const std::list<C2ConstLinearBlock> linearBlocks() const;
+
+    /**
+     * Gets the graphic blocks of this buffer.
+     * \return a constant list of const graphic blocks of this buffer.
+     * \retval empty list if this buffer does not contain graphic block(s).
+     */
+    const std::list<C2ConstGraphicBlock> graphicBlocks() const;
+
+private:
+    class Impl;
+    std::shared_ptr<Impl> mImpl;
+
+protected:
+    // no public constructor
+    // C2BufferData(const std::shared_ptr<const Impl> &impl) : mImpl(impl) {}
+};
+
+/**
+ * C2Buffer: buffer base class. These are always used as shared_ptrs. Though the underlying buffer
+ * objects (native buffers, ion buffers, or dmabufs) are reference-counted by the system,
+ * C2Buffers hold only a single reference.
+ *
+ * These objects cannot be used on the stack.
+ */
+class C2Buffer {
+public:
+    /**
+     * Gets the buffer's data.
+     *
+     * \return the buffer's data.
+     */
+    const C2BufferData data() const;
+
+    /**
+     * These will still work if used in onDeathNotify.
+     */
+#if 0
+    inline std::shared_ptr<C2LinearBuffer> asLinearBuffer() const {
+        return mType == LINEAR ? std::shared_ptr::reinterpret_cast<C2LinearBuffer>(this) : nullptr;
+    }
+
+    inline std::shared_ptr<C2GraphicBuffer> asGraphicBuffer() const {
+        return mType == GRAPHIC ? std::shared_ptr::reinterpret_cast<C2GraphicBuffer>(this) : nullptr;
+    }
+
+    inline std::shared_ptr<C2CircularBuffer> asCircularBuffer() const {
+        return mType == CIRCULAR ? std::shared_ptr::reinterpret_cast<C2CircularBuffer>(this) : nullptr;
+    }
+#endif
+
+    ///@name Pre-destroy notification handling
+    ///@{
+
+    /**
+     * Register for notification just prior to the destruction of this object.
+     */
+    typedef void (*OnDestroyNotify) (const C2Buffer *buf, void *arg);
+
+    /**
+     * Registers for a pre-destroy notification. This is called just prior to the destruction of
+     * this buffer (when this buffer is no longer valid.)
+     *
+     * \param onDestroyNotify   the notification callback
+     * \param arg               an arbitrary parameter passed to the callback
+     *
+     * \retval C2_OK        the registration was successful.
+     * \retval C2_DUPLICATE a notification was already registered for this callback and argument
+     * \retval C2_NO_MEMORY not enough memory to register for this callback
+     * \retval C2_CORRUPTED an unknown error prevented the registration (unexpected)
+     */
+    C2Error registerOnDestroyNotify(OnDestroyNotify *onDestroyNotify, void *arg = nullptr);
+
+    /**
+     * Unregisters a previously registered pre-destroy notification.
+     *
+     * \param onDestroyNotify   the notification callback
+     * \param arg               an arbitrary parameter passed to the callback
+     *
+     * \retval C2_OK        the unregistration was successful.
+     * \retval C2_NOT_FOUND the notification was not found
+     * \retval C2_CORRUPTED an unknown error prevented the registration (unexpected)
+     */
+    C2Error unregisterOnDestroyNotify(OnDestroyNotify *onDestroyNotify, void *arg = nullptr);
+
+    ///@}
+
+    virtual ~C2Buffer() = default;
+
+    ///@name Buffer-specific arbitrary metadata handling
+    ///@{
+
+    /**
+     * Gets the list of metadata associated with this buffer.
+     *
+     * \return a constant list of info objects associated with this buffer.
+     */
+    const std::list<std::shared_ptr<const C2Info>> infos() const;
+
+    /**
+     * Attaches (or updates) an (existing) metadata for this buffer.
+     * If the metadata is stream specific, the stream information will be reset.
+     *
+     * \param info Metadata to update
+     *
+     * \retval C2_OK        the metadata was successfully attached/updated.
+     * \retval C2_NO_MEMORY not enough memory to attach the metadata (this return value is not
+     *                      used if the same kind of metadata is already attached to the buffer).
+     */
+    C2Error setInfo(const std::shared_ptr<C2Info> &info);
+
+    /**
+     * Checks if there is a certain type of metadata attached to this buffer.
+     *
+     * \param index the parameter type of the metadata
+     *
+     * \return true iff there is a metadata with the parameter type attached to this buffer.
+     */
+    bool hasInfo(C2Param::Type index) const;
+    std::shared_ptr<C2Info> removeInfo(C2Param::Type index) const;
+    ///@}
+
+protected:
+    // no public constructor
+    inline C2Buffer() = default;
+
+private:
+//    Type _mType;
+};
+
+/**
+ * An extension of C2Info objects that can contain arbitrary buffer data.
+ *
+ * \note This object is not describable and contains opaque data.
+ */
+class C2InfoBuffer {
+public:
+    /**
+     * Gets the index of this info object.
+     *
+     * \return the parameter index.
+     */
+    const C2Param::Index index() const;
+
+    /**
+     * Gets the buffer's data.
+     *
+     * \return the buffer's data.
+     */
+    const C2BufferData data() const;
+};
+
+/// @}
+
+/**************************************************************************************************
+  ALLOCATIONS
+**************************************************************************************************/
+
+/// \defgroup allocator Allocation and memory placement
+/// @{
+
+/**
+ * Buffer/memory usage bits. These are used by the allocators to select optimal memory type/pool and
+ * buffer layout.
+ *
+ * \note This struct has public fields without getters/setters. All methods are inline.
+ */
+struct C2MemoryUsage {
+// public:
+    // TODO: match these to gralloc1.h
+    enum Consumer : uint64_t {
+        kSoftwareRead        = GRALLOC_USAGE_SW_READ_OFTEN,
+        kRenderScriptRead    = GRALLOC_USAGE_RENDERSCRIPT,
+        kTextureRead         = GRALLOC_USAGE_HW_TEXTURE,
+        kHardwareComposer    = GRALLOC_USAGE_HW_COMPOSER,
+        kHardwareEncoder     = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+        kProtectedRead       = GRALLOC_USAGE_PROTECTED,
+    };
+
+    enum Producer : uint64_t {
+        kSoftwareWrite       = GRALLOC_USAGE_SW_WRITE_OFTEN,
+        kRenderScriptWrite   = GRALLOC_USAGE_RENDERSCRIPT,
+        kTextureWrite        = GRALLOC_USAGE_HW_RENDER,
+        kCompositionTarget   = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER,
+        kHardwareDecoder     = GRALLOC_USAGE_HW_VIDEO_ENCODER,
+        kProtectedWrite      = GRALLOC_USAGE_PROTECTED,
+    };
+
+    uint64_t mConsumer; // e.g. input
+    uint64_t mProducer; // e.g. output
+};
+
+/**
+ * \ingroup linear allocator
+ * 1D allocation interface.
+ */
+class C2LinearAllocation : public _C2LinearCapacityAspect {
+public:
+    /**
+     * Maps a portion of an allocation starting from |offset| with |size| into local process memory.
+     * Stores the starting address into |addr|, or NULL if the operation was unsuccessful.
+     * |fenceFd| is a file descriptor referring to an acquire sync fence object. If it is already
+     * safe to access the buffer contents, then -1.
+     *
+     * \param offset          starting position of the portion to be mapped (this does not have to
+     *                      be page aligned)
+     * \param size            size of the portion to be mapped (this does not have to be page
+     *                      aligned)
+     * \param usage           the desired usage. \todo this must be kSoftwareRead and/or
+     *                      kSoftwareWrite.
+     * \param fenceFd         a pointer to a file descriptor if an async mapping is requested. If
+     *                      not-null, and acquire fence FD will be stored here on success, or -1
+     *                      on failure. If null, the mapping will be synchronous.
+     * \param addr            a pointer to where the starting address of the mapped portion will be
+     *                      stored. On failure, nullptr will be stored here.
+     *
+     * \todo Only one portion can be mapped at the same time - this is true for gralloc, but there
+     *       is no need for this for 1D buffers.
+     * \todo Do we need to support sync operation as we could just wait for the fence?
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_NO_PERMISSION no permission to map the portion
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_NO_MEMORY not enough memory to complete the operation
+     * \retval C2_BAD_VALUE the parameters (offset/size) are invalid or outside the allocation, or
+     *                      the usage flags are invalid (caller error)
+     * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+     */
+    virtual C2Error map(
+            size_t offset, size_t size, C2MemoryUsage usage, int *fenceFd /* nullable */,
+            void **addr /* nonnull */) = 0;
+
+    /**
+     * Unmaps a portion of an allocation at |addr| with |size|. These must be parameters previously
+     * passed to |map|; otherwise, this operation is a no-op.
+     *
+     * \param addr            starting address of the mapped region
+     * \param size            size of the mapped region
+     * \param fenceFd         a pointer to a file descriptor if an async unmapping is requested. If
+     *                      not-null, a release fence FD will be stored here on success, or -1
+     *                      on failure. This fence signals when the original allocation contains
+     *                      any changes that happened to the mapped region. If null, the unmapping
+     *                      will be synchronous.
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_BAD_VALUE the parameters (addr/size) do not correspond to previously mapped
+     *                      regions (caller error)
+     * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+     * \retval C2_NO_PERMISSION no permission to unmap the portion (unexpected - system)
+     */
+    virtual C2Error unmap(void *addr, size_t size, int *fenceFd /* nullable */) = 0;
+
+    /**
+     * Returns true if this is a valid allocation.
+     *
+     * \todo remove?
+     */
+    virtual bool isValid() const = 0;
+
+    /**
+     * Returns a pointer to the allocation handle.
+     */
+    virtual const C2Handle *handle() const = 0;
+
+    /**
+     * Returns true if this is the same allocation as |other|.
+     */
+    virtual bool equals(const std::shared_ptr<C2LinearAllocation> &other) const = 0;
+
+protected:
+    // \todo should we limit allocation directly?
+    C2LinearAllocation(size_t capacity) : _C2LinearCapacityAspect(c2_min(capacity, UINT32_MAX)) {}
+    virtual ~C2LinearAllocation() = default;
+};
+
+/**
+ * \ingroup graphic allocator
+ * 2D allocation interface.
+ */
+class C2GraphicAllocation : public _C2PlanarCapacityAspect {
+public:
+    /**
+     * Maps a rectangular section (as defined by |rect|) of a 2D allocation into local process
+     * memory for flexible access. On success, it fills out |layout| with the plane specifications
+     * and fills the |addr| array with pointers to the first byte of the top-left pixel of each
+     * plane used. Otherwise, it leaves |layout| and |addr| untouched. |fenceFd| is a file
+     * descriptor referring to an acquire sync fence object. If it is already safe to access the
+     * buffer contents, then -1.
+     *
+     * \note Only one portion of the graphic allocation can be mapped at the same time. (This is
+     * from gralloc1 limitation.)
+     *
+     * \param rect            section to be mapped (this does not have to be aligned)
+     * \param usage           the desired usage. \todo this must be kSoftwareRead and/or
+     *                      kSoftwareWrite.
+     * \param fenceFd         a pointer to a file descriptor if an async mapping is requested. If
+     *                      not-null, and acquire fence FD will be stored here on success, or -1
+     *                      on failure. If null, the mapping will be synchronous.
+     * \param layout          a pointer to where the mapped planes' descriptors will be
+     *                      stored. On failure, nullptr will be stored here.
+     *
+     * \todo Do we need to support sync operation as we could just wait for the fence?
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_NO_PERMISSION no permission to map the section
+     * \retval C2_ALREADY_EXISTS there is already a mapped region (caller error)
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_NO_MEMORY not enough memory to complete the operation
+     * \retval C2_BAD_VALUE the parameters (rect) are invalid or outside the allocation, or the
+     *                      usage flags are invalid (caller error)
+     * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+
+     */
+    virtual C2Error map(
+            C2Rect rect, C2MemoryUsage usage, int *fenceFd,
+            // TODO: return <addr, size> buffers with plane sizes
+            C2PlaneLayout *layout /* nonnull */, uint8_t **addr /* nonnull */) = 0;
+
+    /**
+     * Unmaps the last mapped rectangular section.
+     *
+     * \param fenceFd         a pointer to a file descriptor if an async unmapping is requested. If
+     *                      not-null, a release fence FD will be stored here on success, or -1
+     *                      on failure. This fence signals when the original allocation contains
+     *                      any changes that happened to the mapped section. If null, the unmapping
+     *                      will be synchronous.
+     *
+     * \retval C2_OK        the operation was successful
+     * \retval C2_TIMED_OUT the operation timed out
+     * \retval C2_NOT_FOUND there is no mapped region (caller error)
+     * \retval C2_CORRUPTED some unknown error prevented the operation from completing (unexpected)
+     * \retval C2_NO_PERMISSION no permission to unmap the section (unexpected - system)
+     */
+    virtual C2Error unmap(C2Fence *fenceFd /* nullable */) = 0;
+
+    /**
+     * Returns true if this is a valid allocation.
+     *
+     * \todo remove?
+     */
+    virtual bool isValid() const = 0;
+
+    /**
+     * Returns a pointer to the allocation handle.
+     */
+    virtual const C2Handle *handle() const = 0;
+
+    /**
+     * Returns true if this is the same allocation as |other|.
+     */
+    virtual bool equals(const std::shared_ptr<const C2GraphicAllocation> &other) = 0;
+
+protected:
+    virtual ~C2GraphicAllocation();
+};
+
+/**
+ *  Allocators are used by the framework to allocate memory (allocations) for buffers. They can
+ *  support either 1D or 2D allocations.
+ *
+ *  \note In theory they could support both, but in practice, we will use only one or the other.
+ *
+ *  Never constructed on stack.
+ *
+ *  Allocators are provided by vendors.
+ */
+class C2Allocator {
+public:
+    /**
+     * Allocates a 1D allocation of given |capacity| and |usage|. If successful, the allocation is
+     * stored in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+     *
+     * \param capacity        the size of requested allocation (the allocation could be slightly
+     *                      larger, e.g. to account for any system-required alignment)
+     * \param usage           the memory usage info for the requested allocation. \note that the
+     *                      returned allocation may be later used/mapped with different usage.
+     *                      The allocator should layout the buffer to be optimized for this usage,
+     *                      but must support any usage. One exception: protected buffers can
+     *                      only be used in a protected scenario.
+     * \param allocation      pointer to where the allocation shall be stored on success. nullptr
+     *                      will be stored here on failure
+     *
+     * \retval C2_OK        the allocation was successful
+     * \retval C2_NO_MEMORY not enough memory to complete the allocation
+     * \retval C2_TIMED_OUT the allocation timed out
+     * \retval C2_NO_PERMISSION     no permission to complete the allocation
+     * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
+     * \retval C2_UNSUPPORTED       this allocator does not support 1D allocations
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+     */
+    virtual C2Error allocateLinearBuffer(
+            uint32_t capacity __unused, C2MemoryUsage usage __unused,
+            std::shared_ptr<C2LinearAllocation> *allocation /* nonnull */) {
+        *allocation = nullptr;
+        return C2_UNSUPPORTED;
+    }
+
+    /**
+     * (Re)creates a 1D allocation from a native |handle|. If successful, the allocation is stored
+     * in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+     *
+     * \param handle      the handle for the existing allocation
+     * \param allocation  pointer to where the allocation shall be stored on success. nullptr
+     *                  will be stored here on failure
+     *
+     * \retval C2_OK        the allocation was recreated successfully
+     * \retval C2_NO_MEMORY not enough memory to recreate the allocation
+     * \retval C2_TIMED_OUT the recreation timed out (unexpected)
+     * \retval C2_NO_PERMISSION     no permission to recreate the allocation
+     * \retval C2_BAD_VALUE invalid handle (caller error)
+     * \retval C2_UNSUPPORTED       this allocator does not support 1D allocations
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+     */
+    virtual C2Error recreateLinearBuffer(
+            const C2Handle *handle __unused,
+            std::shared_ptr<C2LinearAllocation> *allocation /* nonnull */) {
+        *allocation = nullptr;
+        return C2_UNSUPPORTED;
+    }
+
+    /**
+     * Allocates a 2D allocation of given |width|, |height|, |format| and |usage|. If successful,
+     * the allocation is stored in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+     *
+     * \param width           the width of requested allocation (the allocation could be slightly
+     *                      larger, e.g. to account for any system-required alignment)
+     * \param height          the height of requested allocation (the allocation could be slightly
+     *                      larger, e.g. to account for any system-required alignment)
+     * \param format          the pixel format of requested allocation. This could be a vendor
+     *                      specific format.
+     * \param usage           the memory usage info for the requested allocation. \note that the
+     *                      returned allocation may be later used/mapped with different usage.
+     *                      The allocator should layout the buffer to be optimized for this usage,
+     *                      but must support any usage. One exception: protected buffers can
+     *                      only be used in a protected scenario.
+     * \param allocation      pointer to where the allocation shall be stored on success. nullptr
+     *                      will be stored here on failure
+     *
+     * \retval C2_OK        the allocation was successful
+     * \retval C2_NO_MEMORY not enough memory to complete the allocation
+     * \retval C2_TIMED_OUT the allocation timed out
+     * \retval C2_NO_PERMISSION     no permission to complete the allocation
+     * \retval C2_BAD_VALUE width, height, format or usage are not supported (invalid) (caller error)
+     * \retval C2_UNSUPPORTED       this allocator does not support 2D allocations
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+     */
+    virtual C2Error allocateGraphicBuffer(
+            uint32_t width __unused, uint32_t height __unused, uint32_t format __unused,
+            C2MemoryUsage usage __unused,
+            std::shared_ptr<C2GraphicAllocation> *allocation /* nonnull */) {
+        *allocation = nullptr;
+        return C2_UNSUPPORTED;
+    }
+
+    /**
+     * (Re)creates a 2D allocation from a native handle.  If successful, the allocation is stored
+     * in |allocation|. Otherwise, |allocation| is set to 'nullptr'.
+     *
+     * \param handle      the handle for the existing allocation
+     * \param allocation  pointer to where the allocation shall be stored on success. nullptr
+     *                  will be stored here on failure
+     *
+     * \retval C2_OK        the allocation was recreated successfully
+     * \retval C2_NO_MEMORY not enough memory to recreate the allocation
+     * \retval C2_TIMED_OUT the recreation timed out (unexpected)
+     * \retval C2_NO_PERMISSION     no permission to recreate the allocation
+     * \retval C2_BAD_VALUE invalid handle (caller error)
+     * \retval C2_UNSUPPORTED       this allocator does not support 2D allocations
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during recreation (unexpected)
+     */
+    virtual C2Error recreateGraphicBuffer(
+            const C2Handle *handle __unused,
+            std::shared_ptr<C2GraphicAllocation> *allocation /* nonnull */) {
+        *allocation = nullptr;
+        return C2_UNSUPPORTED;
+    }
+
+protected:
+    C2Allocator() = default;
+
+    virtual ~C2Allocator() = default;
+};
+
+/**
+ *  Block allocators are used by components to allocate memory for output buffers. They can
+ *  support either linear (1D), circular (1D) or graphic (2D) allocations.
+ *
+ *  Never constructed on stack.
+ *
+ *  Block allocators are provided by the framework.
+ */
+class C2BlockAllocator {
+public:
+    /**
+     * Allocates a linear writeable block of given |capacity| and |usage|. If successful, the
+     * block is stored in |block|. Otherwise, |block| is set to 'nullptr'.
+     *
+     * \param capacity        the size of requested block.
+     * \param usage           the memory usage info for the requested allocation. \note that the
+     *                      returned allocation may be later used/mapped with different usage.
+     *                      The allocator shall lay out the buffer to be optimized for this usage,
+     *                      but must support any usage. One exception: protected buffers can
+     *                      only be used in a protected scenario.
+     * \param block      pointer to where the allocated block shall be stored on success. nullptr
+     *                      will be stored here on failure
+     *
+     * \retval C2_OK        the allocation was successful
+     * \retval C2_NO_MEMORY not enough memory to complete the allocation
+     * \retval C2_TIMED_OUT the allocation timed out
+     * \retval C2_NO_PERMISSION     no permission to complete the allocation
+     * \retval C2_BAD_VALUE capacity or usage are not supported (invalid) (caller error)
+     * \retval C2_UNSUPPORTED       this allocator does not support linear allocations
+     * \retval C2_CORRUPTED some unknown, unrecoverable error occured during allocation (unexpected)
+     */
+    virtual C2Error allocateLinearBlock(
+            uint32_t capacity __unused, C2MemoryUsage usage __unused,
+            std::shared_ptr<C2LinearBlock> *block /* nonnull */) {
+        *block = nullptr;
+        return C2_UNSUPPORTED;
+    }
+
+    /**
+     * Allocates a circular writeable block of given |capacity| and |usage|. If successful, the
+     * block is stored in |block|. Otherwise, |block| is set to 'nullptr'.
+     *
+     * \param capacity        the size of requested circular block. (the allocation could be slightly
+     *                      larger, e.g. to account for any system-required alignment)
+     * \param usage           the memory usage info for the requested allocation. \note that the
+     *                      returned allocation may be later used/mapped with different usage.
+     *                      The allocator shall lay out the buffer to be optimized for this usage,
+     *                      but must support any usage. One exception: protected buffers can
+     *                      only be used in a protected scenario.
+     * \param block      pointer to where the allocated block shall be stored on success. nullptr
+     *                      will be stored here on failure
+     *
+     * \retval C2_OK            the allocation was successful
+     * \retval C2_NO_MEMORY     not enough memory to complete the allocation
+     * \retval C2_TIMED_OUT     the allocation timed out
+     * \retval C2_NO_PERMISSION     no permission to complete the allocation
+     * \retval C2_BAD_VALUE     capacity or usage are not supported (invalid) (caller error)
+     * \retval C2_UNSUPPORTED   this allocator does not support circular allocations
+     * \retval C2_CORRUPTED     some unknown, unrecoverable error occured during allocation (unexpected)
+     */
+    virtual C2Error allocateCircularBlock(
+            uint32_t capacity __unused, C2MemoryUsage usage __unused,
+            std::shared_ptr<C2CircularBlock> *block /* nonnull */) {
+        *block = nullptr;
+        return C2_UNSUPPORTED;
+    }
+
+    /**
+     * Allocates a 2D graphic block of given |width|, |height|, |format| and |usage|. If successful,
+     * the allocation is stored in |block|. Otherwise, |block| is set to 'nullptr'.
+     *
+     * \param width           the width of requested allocation (the allocation could be slightly
+     *                      larger, e.g. to account for any system-required alignment)
+     * \param height          the height of requested allocation (the allocation could be slightly
+     *                      larger, e.g. to account for any system-required alignment)
+     * \param format          the pixel format of requested allocation. This could be a vendor
+     *                      specific format.
+     * \param usage           the memory usage info for the requested allocation. \note that the
+     *                      returned allocation may be later used/mapped with different usage.
+     *                      The allocator should layout the buffer to be optimized for this usage,
+     *                      but must support any usage. One exception: protected buffers can
+     *                      only be used in a protected scenario.
+     * \param block      pointer to where the allocation shall be stored on success. nullptr
+     *                      will be stored here on failure
+     *
+     * \retval C2_OK            the allocation was successful
+     * \retval C2_NO_MEMORY     not enough memory to complete the allocation
+     * \retval C2_TIMED_OUT     the allocation timed out
+     * \retval C2_NO_PERMISSION     no permission to complete the allocation
+     * \retval C2_BAD_VALUE     width, height, format or usage are not supported (invalid) (caller error)
+     * \retval C2_UNSUPPORTED   this allocator does not support 2D allocations
+     * \retval C2_CORRUPTED     some unknown, unrecoverable error occured during allocation (unexpected)
+     */
+    virtual C2Error allocateGraphicBlock(
+            uint32_t width __unused, uint32_t height __unused, uint32_t format __unused,
+            C2MemoryUsage usage __unused,
+            std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
+        *block = nullptr;
+        return C2_UNSUPPORTED;
+    }
+
+protected:
+    C2BlockAllocator() = default;
+
+    virtual ~C2BlockAllocator() = default;
+};
+
+/// @}
+
+/// \cond INTERNAL
+
+/// \todo These are no longer used
+
+/// \addtogroup linear
+/// @{
+
+/** \deprecated */
+class C2LinearBuffer
+    : public C2Buffer, public _C2LinearRangeAspect,
+      public std::enable_shared_from_this<C2LinearBuffer> {
+public:
+    /** \todo what is this? */
+    const C2Handle *handle() const;
+
+protected:
+    inline C2LinearBuffer(const C2ConstLinearBlock &block);
+
+private:
+    class Impl;
+    Impl *mImpl;
+};
+
+class C2ReadCursor;
+
+class C2WriteCursor {
+public:
+    uint32_t remaining() const; // remaining data to be read
+    void commit(); // commits the current position. discard data before current position
+    void reset() const;  // resets position to the last committed position
+    // slices off at most |size| bytes, and moves cursor ahead by the number of bytes
+    // sliced off.
+    C2ReadCursor slice(uint32_t size) const;
+    // slices off at most |size| bytes, and moves cursor ahead by the number of bytes
+    // sliced off.
+    C2WriteCursor reserve(uint32_t size);
+    // bool read(T&);
+    // bool write(T&);
+    C2Fence waitForSpace(uint32_t size);
+};
+
+/// @}
+
+/// \addtogroup graphic
+/// @{
+
+struct C2ColorSpace {
+//public:
+    enum Standard {
+        BT601,
+        BT709,
+        BT2020,
+        // TODO
+    };
+
+    enum Range {
+        LIMITED,
+        FULL,
+        // TODO
+    };
+
+    enum TransferFunction {
+        BT709Transfer,
+        BT2020Transfer,
+        HybridLogGamma2,
+        HybridLogGamma4,
+        // TODO
+    };
+};
+
+/** \deprecated */
+class C2GraphicBuffer : public C2Buffer {
+public:
+    // constant attributes
+    inline uint32_t width() const  { return mWidth; }
+    inline uint32_t height() const { return mHeight; }
+    inline uint32_t format() const { return mFormat; }
+    inline const C2MemoryUsage usage() const { return mUsage; }
+
+    // modifiable attributes
+
+
+    virtual const C2ColorSpace colorSpace() const = 0;
+    // best effort
+    virtual void setColorSpace_be(const C2ColorSpace &colorSpace) = 0;
+    virtual bool setColorSpace(const C2ColorSpace &colorSpace) = 0;
+
+    const C2Handle *handle() const;
+
+protected:
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint32_t mFormat;
+    C2MemoryUsage mUsage;
+
+    class Impl;
+    Impl *mImpl;
+};
+
+/// @}
+
+/// \endcond
+
+/// @}
+
+}  // namespace android
+
+#endif  // C2BUFFER_H_
diff --git a/media/libstagefright/codec2/include/C2Component.h b/media/libstagefright/codec2/include/C2Component.h
new file mode 100644
index 0000000..1ee9302
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2Component.h
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2COMPONENT_H_
+
+#define C2COMPONENT_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <list>
+#include <memory>
+#include <vector>
+#include <functional>
+
+#include <C2Param.h>
+#include <C2Work.h>
+
+namespace android {
+
+/// \defgroup components Components
+/// @{
+
+class C2Component;
+
+class C2ComponentListener {
+public:
+    virtual void onWorkDone(std::weak_ptr<C2Component> component,
+                            std::vector<std::unique_ptr<C2Work>> workItems) = 0;
+
+    virtual void onTripped(std::weak_ptr<C2Component> component,
+                           std::vector<std::shared_ptr<C2SettingResult>> settingResult) = 0;
+
+    virtual void onError(std::weak_ptr<C2Component> component,
+                         uint32_t errorCode) = 0;
+
+    // virtual void onTunnelReleased(<from>, <to>) = 0;
+
+    // virtual void onComponentReleased(<id>) = 0;
+
+protected:
+    virtual ~C2ComponentListener();
+};
+
+/**
+ * Component interface object. This object contains all of the configuration of a potential or
+ * actual component. It can be created and used independently of an actual C2Component instance to
+ * query support and parameters for various component settings and configurations for a potential
+ * component. Actual components also expose this interface.
+ */
+
+class C2ComponentInterface {
+public:
+    // ALWAYS AVAILABLE METHODS
+    // =============================================================================================
+
+    /**
+     * Returns the name of this component or component interface object.
+     * This is a unique name for this component or component interface 'class'; however, multiple
+     * instances of this component SHALL have the same name.
+     *
+     * This method MUST be supported in any state. This call does not change the state nor the
+     * internal states of the component.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return the name of this component or component interface object.
+     * \retval an empty string if there was not enough memory to allocate the actual name.
+     */
+    virtual C2String getName() const = 0;
+
+    /**
+     * Returns a unique ID for this component or interface object.
+     * This ID is used as work targets, unique work IDs, and when configuring tunneling.
+     *
+     * This method MUST be supported in any state. This call does not change the state nor the
+     * internal states of the component.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \return a unique node ID for this component or component interface instance.
+     */
+    virtual node_id getId() const = 0;
+
+    /**
+     * Queries a set of parameters from the component or interface object.
+     * Querying is performed at best effort: the component SHALL query all supported parameters and
+     * skip unsupported ones, or heap allocated parameters that could not be allocated. Any errors
+     * are communicated in the return value. Additionally, preallocated (e.g. stack) parameters that
+     * could not be queried are invalidated. Parameters to be allocated on the heap are omitted from
+     * the result.
+     *
+     * \note Parameter values do not depend on the order of query.
+     *
+     * \todo This method cannot be used to query info-buffers. Is that a problem?
+     *
+     * This method MUST be supported in any state. This call does not change the state nor the
+     * internal states of the component.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \param[in,out] stackParams   a list of params queried. These are initialized specific to each
+     *                      setting; e.g. size and index are set and rest of the members are
+     *                      cleared.
+     *                      \note Flexible settings that are of incorrect size will be invalidated.
+     * \param[in] heapParamIndices a vector of param indices for params to be queried and returned on the
+     *                      heap. These parameters will be returned in heapParams. Unsupported param
+     *                      indices will be ignored.
+     * \param[out] heapParams    a list of params where to which the supported heap parameters will be
+     *                      appended in the order they appear in heapParamIndices.
+     *
+     * \retval C2_OK        all parameters could be queried
+     * \retval C2_BAD_INDEX all supported parameters could be queried, but some parameters were not
+     *                      supported
+     * \retval C2_NO_MEMORY could not allocate memory for a supported parameter
+     * \retval C2_CORRUPTED some unknown error prevented the querying of the parameters
+     *                      (unexpected)
+     */
+    virtual status_t query_nb(
+        const std::vector<C2Param* const> &stackParams,
+        const std::vector<C2Param::Index> &heapParamIndices,
+        std::vector<std::unique_ptr<C2Param>>* const heapParams) const = 0;
+
+    /**
+     * Sets a set of parameters for the component or interface object.
+     * Tuning is performed at best effort: the component SHALL update all supported configuration at
+     * best effort (unless configured otherwise) and skip unsupported ones. Any errors are
+     * communicated in the return value and in |failures|.
+     *
+     * \note Parameter tuning DOES depend on the order of the tuning parameters. E.g. some parameter
+     * update may allow some subsequent parameter update.
+     *
+     * This method MUST be supported in any state.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \param[in,out] params          a list of parameter updates. These will be updated to the actual
+     *                      parameter values after the updates (this is because tuning is performed
+     *                      at best effort).
+     *                      \todo params that could not be updated are not marked here, so are
+     *                      confusing - are they "existing" values or intended to be configured
+     *                      values?
+     * \param[out] failures        a list of parameter failures
+     *
+     * \retval C2_OK        all parameters could be updated successfully
+     * \retval C2_BAD_INDEX all supported parameters could be updated successfully, but some
+     *                      parameters were not supported
+     * \retval C2_BAD_VALUE some supported parameters could not be updated successfully because
+     *                      they contained unsupported values. These are returned in |failures|.
+     * \retval C2_NO_MEMORY some supported parameters could not be updated successfully because
+     *                      they contained unsupported values, but could not allocate a failure
+     *                      object for them.
+     * \retval C2_CORRUPTED some unknown error prevented the update of the parameters
+     *                      (unexpected)
+     */
+    virtual status_t config_nb(
+            const std::vector<C2Param* const> &params,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
+
+    /**
+     * Atomically sets a set of parameters for the component or interface object.
+     *
+     * \note This method is used mainly for reserving resources for a component.
+     *
+     * The component SHALL update all supported configuration at
+     * best effort(TBD) (unless configured otherwise) and skip unsupported ones. Any errors are
+     * communicated in the return value and in |failures|.
+     *
+     * \note Parameter tuning DOES depend on the order of the tuning parameters. E.g. some parameter
+     * update may allow some subsequent parameter update.
+     *
+     * This method MUST be supported in any state.
+     *
+     * This method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \param params[in,out]          a list of parameter updates. These will be updated to the actual
+     *                      parameter values after the updates (this is because tuning is performed
+     *                      at best effort).
+     *                      \todo params that could not be updated are not marked here, so are
+     *                      confusing - are they "existing" values or intended to be configured
+     *                      values?
+     * \param failures[out]        a list of parameter failures
+     *
+     * \retval C2_OK        all parameters could be updated successfully
+     * \retval C2_BAD_INDEX all supported parameters could be updated successfully, but some
+     *                      parameters were not supported
+     * \retval C2_BAD_VALUE some supported parameters could not be updated successfully because
+     *                      they contained unsupported values. These are returned in |failures|.
+     * \retval C2_NO_MEMORY some supported parameters could not be updated successfully because
+     *                      they contained unsupported values, but could not allocate a failure
+     *                      object for them.
+     * \retval C2_CORRUPTED some unknown error prevented the update of the parameters
+     *                      (unexpected)
+     */
+    virtual status_t commit_sm(
+            const std::vector<C2Param* const> &params,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) = 0;
+
+    // TUNNELING
+    // =============================================================================================
+
+    /**
+     * Creates a tunnel from this component to the target component.
+     *
+     * If the component is successfully created, subsequent work items queued may include a
+     * tunneled path between these components.
+     *
+     * This method MUST be supported in any state.
+     *
+     * This method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \retval C2_OK        the tunnel was successfully created
+     * \retval C2_BAD_INDEX the target component does not exist
+     * \retval C2_ALREADY_EXIST the tunnel already exists
+     * \retval C2_UNSUPPORTED  the tunnel is not supported
+     *
+     * \retval C2_TIMED_OUT could not create the tunnel within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the tunnel (unexpected)
+     */
+    virtual status_t createTunnel_sm(node_id targetComponent) = 0;
+
+    /**
+     * Releases a tunnel from this component to the target component.
+     *
+     * The release of a tunnel is delayed while there are pending work items for the tunnel.
+     * After releasing a tunnel, subsequent work items queued MUST NOT include a tunneled
+     * path between these components.
+     *
+     * This method MUST be supported in any state.
+     *
+     * This method may be momentarily blocking, but MUST return within 5ms.
+     *
+     * \retval C2_OK        the tunnel was marked for release successfully
+     * \retval C2_BAD_INDEX the target component does not exist
+     * \retval C2_NOT_FOUND the tunnel does not exist
+     *
+     * \retval C2_TIMED_OUT could not mark the tunnel for release within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the release of the tunnel (unexpected)
+     */
+    virtual status_t releaseTunnel_sm(node_id targetComponent) = 0;
+
+
+    // REFLECTION MECHANISM (USED FOR EXTENSION)
+    // =============================================================================================
+
+    /**
+     * Returns the parameter reflector.
+     *
+     * This is used to describe parameter fields.
+     *
+     * \return a shared parameter reflector object.
+     */
+    virtual std::shared_ptr<C2ParamReflector> getParamReflector() const = 0;
+
+    /**
+     * Returns the set of supported parameters.
+     *
+     * \param[out] params a vector of supported parameters will be appended to this vector.
+     *
+     * \retval C2_OK        the operation completed successfully.
+     * \retval C2_NO_MEMORY not enough memory to complete this method.
+     */
+    virtual status_t getSupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const = 0;
+
+    /**
+     *
+     * \todo should this take a list considering that setting some fields may further limit other
+     * fields in the same list?
+     */
+    virtual status_t getSupportedValues(
+            const std::vector<const C2ParamField> fields,
+            std::vector<C2FieldSupportedValues>* const values) const = 0;
+
+    virtual ~C2ComponentInterface() = default;
+};
+
+class C2Component {
+public:
+    // METHODS AVAILABLE WHEN RUNNING
+    // =============================================================================================
+
+    /**
+     * Queues up work for the component.
+     *
+     * This method MUST be supported in running (including tripped) states.
+     *
+     * This method MUST be "non-blocking" and return within 1ms
+     *
+     * It is acceptable for this method to return OK and return an error value using the
+     * onWorkDone() callback.
+     *
+     * \retval C2_OK        the work was successfully queued
+     * \retval C2_BAD_INDEX some component(s) in the work do(es) not exist
+     * \retval C2_UNSUPPORTED  the components are not tunneled
+     *
+     * \retval C2_NO_MEMORY not enough memory to queue the work
+     * \retval C2_CORRUPTED some unknown error prevented queuing the work (unexpected)
+     */
+    virtual status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) = 0;
+
+    /**
+     * Announces a work to be queued later for the component. This reserves a slot for the queue
+     * to ensure correct work ordering even if the work is queued later.
+     *
+     * This method MUST be supported in running (including tripped) states.
+     *
+     * This method MUST be "non-blocking" and return within 1 ms
+     *
+     * \retval C2_OK        the work announcement has been successfully recorded
+     * \retval C2_BAD_INDEX some component(s) in the work outline do(es) not exist
+     * \retval C2_UNSUPPORTED  the componentes are not tunneled
+     *
+     * \retval C2_NO_MEMORY not enough memory to record the work announcement
+     * \retval C2_CORRUPTED some unknown error prevented recording the announcement (unexpected)
+     *
+     * \todo Can this be rolled into queue_nb?
+     */
+    virtual status_t announce_nb(const std::vector<C2WorkOutline> &items) = 0;
+
+    /**
+     * Discards and abandons any pending work for the component, and optionally any component
+     * downstream.
+     *
+     * \todo define this: we could flush all work before last item queued for component across all
+     *                    components linked to this; flush only work items that are queued to this
+     *                    component
+     * \todo return work # of last flushed item; or all flushed (but not returned items)
+     * \todo we could make flush take a work item and flush all work before/after that item to allow
+     *       TBD (slicing/seek?)
+     * \todo we could simply take a list of numbers and flush those... this is bad for decoders
+     *       also, what would happen to fine grade references?
+     *
+     * This method MUST be supported in running (including tripped) states.
+     *
+     * This method may be momentarily blocking, but must return within 5ms.
+     *
+     * Work that could be immediately abandoned/discarded SHALL be returned in |flushedWork|; this
+     * can be done in an arbitrary order.
+     *
+     * Work that could not be abandoned or discarded immediately SHALL be marked to be
+     * discarded at the earliest opportunity, and SHALL be returned via the onWorkDone() callback.
+     *
+     * \param flushThrough    flush work from this component and all components connected downstream
+     *                      from it via tunneling.
+     *
+     * \retval C2_OK        the work announcement has been successfully recorded
+     * \retval C2_TIMED_OUT the flush could not be completed within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented flushing from completion (unexpected)
+     */
+    virtual status_t flush_sm(bool flushThrough, std::list<std::unique_ptr<C2Work>>* const flushedWork) = 0;
+
+    /**
+     * Drains the component, and optionally downstream components
+     *
+     * \todo define this; we could place EOS to all upstream components, just this component, or
+     *       all upstream and downstream component.
+     * \todo should EOS carry over to downstream components?
+     *
+     * Marks last work item as "end-of-stream", so component is notified not to wait for further
+     * work before it processes work already queued. This method is called to set the end-of-stream
+     * flag after work has been queued. Client can continue to queue further work immediately after
+     * this method returns.
+     *
+     * This method MUST be supported in running (including tripped) states.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * Work that is completed SHALL be returned via the onWorkDone() callback.
+     *
+     * \param drainThrough    marks the last work item with a persistent "end-of-stream" marker that
+     *                      will drain downstream components.
+     *
+     * \todo this may confuse work-ordering downstream; could be an mode enum
+     *
+     * \retval C2_OK        the work announcement has been successfully recorded
+     * \retval C2_TIMED_OUT the flush could not be completed within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented flushing from completion (unexpected)
+     */
+    virtual status_t drain_nb(bool drainThrough) = 0;
+
+    // STATE CHANGE METHODS
+    // =============================================================================================
+
+    /**
+     * Starts the component.
+     *
+     * This method MUST be supported in stopped state.
+     *
+     * \todo This method MUST return within 500ms. Seems this should be able to return quickly, as
+     * there are no immediate guarantees. Though there are guarantees for responsiveness immediately
+     * after start returns.
+     *
+     * \todo Could we just start a ComponentInterface to get a Component?
+     *
+     * \retval C2_OK        the work announcement has been successfully recorded
+     * \retval C2_NO_MEMORY not enough memory to start the component
+     * \retval C2_TIMED_OUT the component could not be started within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented starting the component (unexpected)
+     */
+    virtual status_t start() = 0;
+
+    /**
+     * Stops the component.
+     *
+     * This method MUST be supported in running (including tripped) state.
+     *
+     * This method MUST return withing 500ms.
+     *
+     * Upon this call, all pending work SHALL be abandoned.
+     *
+     * \todo should this return completed work, since client will just free it? Perhaps just to
+     * verify accounting.
+     *
+     * This does not alter any settings and tunings that may have resulted in a tripped state.
+     * (Is this material given the definition? Perhaps in case we want to start again.)
+     */
+    virtual status_t stop() = 0;
+
+    /**
+     * Resets the component.
+     *
+     * This method MUST be supported in running (including tripped) state.
+     *
+     * This method MUST be supported during any other call (\todo or just blocking ones?)
+     *
+     * This method MUST return withing 500ms.
+     *
+     * After this call returns all work is/must be abandoned, all references should be released.
+     *
+     * \todo should this return completed work, since client will just free it? Also, if it unblocks
+     * a stop, where should completed work be returned?
+     *
+     * This brings settings back to their default - "guaranteeing" no tripped space.
+     *
+     * \todo reclaim support - it seems that since ownership is passed, this will allow reclaiming stuff.
+     */
+    virtual void reset() = 0;
+
+    /**
+     * Releases the component.
+     *
+     * This method MUST be supported in any state. (\todo Or shall we force reset() first to bring
+     * to a known state?)
+     *
+     * This method MUST return withing 500ms.
+     *
+     * \todo should this return completed work, since client will just free it? Also, if it unblocks
+     * a stop, where should completed work be returned?
+     *
+     * TODO: does it matter if this call has a short time limit? Yes, as upon return all references
+     * shall be abandoned.
+     */
+    virtual void release() = 0;
+
+    /**
+     * Returns the interface for this component.
+     *
+     * \return the component interface
+     */
+    virtual std::shared_ptr<C2ComponentInterface> intf() = 0;
+
+protected:
+    virtual ~C2Component() = default;
+};
+
+class C2FrameInfoParser {
+public:
+    /**
+     * \return the content type supported by this info parser.
+     *
+     * \todo this may be redundant
+     */
+    virtual C2StringLiteral getType() const = 0;
+
+    /**
+     * \return a vector of supported parameter indices parsed by this info parser.
+     *
+     * \todo sticky vs. non-sticky params? this may be communicated by param-reflector.
+     */
+    virtual const std::vector<C2Param::Index> getParsedParams() const = 0;
+
+    /**
+     * Resets this info parser. This brings this parser to its initial state after creation.
+     *
+     * This method SHALL return within 5ms.
+     *
+     * \retval C2_OK        the info parser was reset
+     * \retval C2_TIMED_OUT could not reset the parser within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the resetting of the parser (unexpected)
+     */
+    virtual status_t reset() { return C2_OK; }
+
+    virtual status_t parseFrame(C2BufferPack &frame);
+
+    virtual ~C2FrameInfoParser() = default;
+};
+
+struct C2ComponentInfo {
+    // TBD
+
+};
+
+class C2AllocatorStore {
+public:
+    // TBD
+
+    enum Type {
+        LINEAR,     ///< basic linear allocator type
+        GRALLOC,    ///< basic gralloc allocator type
+    };
+
+    /**
+     * Creates an allocator.
+     *
+     * \param type      the type of allocator to create
+     * \param allocator shared pointer where the created allocator is stored. Cleared on failure
+     *                  and updated on success.
+     *
+     * \retval C2_OK        the allocator was created successfully
+     * \retval C2_TIMED_OUT could not create the allocator within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the allocator (unexpected)
+     *
+     * \retval C2_NOT_FOUND no such allocator
+     * \retval C2_NO_MEMORY not enough memory to create the allocator
+     */
+    virtual status_t createAllocator(Type type, std::shared_ptr<C2Allocator>* const allocator) = 0;
+
+    virtual ~C2AllocatorStore() = default;
+};
+
+class C2ComponentStore {
+    /**
+     * Creates a component.
+     *
+     * This method SHALL return within 100ms.
+     *
+     * \param name          name of the component to create
+     * \param component     shared pointer where the created component is stored. Cleared on
+     *                      failure and updated on success.
+     *
+     * \retval C2_OK        the component was created successfully
+     * \retval C2_TIMED_OUT could not create the component within the time limit (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the component (unexpected)
+     *
+     * \retval C2_NOT_FOUND no such component
+     * \retval C2_NO_MEMORY not enough memory to create the component
+     */
+    virtual status_t createComponent(C2String name, std::shared_ptr<C2Component>* const component);
+
+    /**
+     * Creates a component interface.
+     *
+     * This method SHALL return within 100ms.
+     *
+     * \param name          name of the component interface to create
+     * \param interface     shared pointer where the created interface is stored
+     *
+     * \retval C2_OK        the component interface was created successfully
+     * \retval C2_TIMED_OUT could not create the component interface within the time limit
+     *                      (unexpected)
+     * \retval C2_CORRUPTED some unknown error prevented the creation of the component interface
+     *                      (unexpected)
+     *
+     * \retval C2_NOT_FOUND no such component interface
+     * \retval C2_NO_MEMORY not enough memory to create the component interface
+     *
+     * \todo Do we need an interface, or could this just be a component that is never started?
+     */
+    virtual status_t createInterface(C2String name, std::shared_ptr<C2ComponentInterface>* const interface);
+
+    /**
+     * Returns the list of components supported by this component store.
+     *
+     * This method SHALL return within 1ms.
+     *
+     * \retval vector of component information.
+     */
+    virtual std::vector<std::unique_ptr<const C2ComponentInfo>> getComponents();
+
+    // -------------------------------------- UTILITY METHODS --------------------------------------
+
+    // on-demand buffer layout conversion (swizzling)
+    virtual status_t copyBuffer(std::shared_ptr<C2GraphicBuffer> src, std::shared_ptr<C2GraphicBuffer> dst);
+
+    // status_t selectPreferredColor(formats<A>, formats<B>);
+
+    // GLOBAL SETTINGS
+    // system-wide stride & slice-height (???)
+
+    /**
+     * Queries a set of system-wide parameters.
+     * Querying is performed at best effort: the store SHALL query all supported parameters and
+     * skip unsupported ones, or heap allocated parameters that could not be allocated. Any errors
+     * are communicated in the return value. Additionally, preallocated (e.g. stack) parameters that
+     * could not be queried are invalidated. Parameters to be allocated on the heap are omitted from
+     * the result.
+     *
+     * \note Parameter values do not depend on the order of query.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \param stackParams     a list of params queried. These are initialized specific to each
+     *                      setting; e.g. size and index are set and rest of the members are
+     *                      cleared.
+     *                      NOTE: Flexible settings that are of incorrect size will be invalidated.
+     * \param heapParamIndices a vector of param indices for params to be queried and returned on the
+     *                      heap. These parameters will be returned in heapParams. Unsupported param
+     *                      indices will be ignored.
+     * \param heapParams      a list of params where to which the supported heap parameters will be
+     *                      appended in the order they appear in heapParamIndices.
+     *
+     * \retval C2_OK        all parameters could be queried
+     * \retval C2_BAD_INDEX all supported parameters could be queried, but some parameters were not
+     *                      supported
+     * \retval C2_NO_MEMORY could not allocate memory for a supported parameter
+     * \retval C2_CORRUPTED some unknown error prevented the querying of the parameters
+     *                      (unexpected)
+     */
+    virtual status_t query_nb(
+        const std::vector<C2Param* const> &stackParams,
+        const std::vector<C2Param::Index> &heapParamIndices,
+        std::vector<std::unique_ptr<C2Param>>* const heapParams) = 0;
+
+    /**
+     * Sets a set of system-wide parameters.
+     *
+     * \note There are no settable system-wide parameters defined thus far, but may be added in the
+     * future.
+     *
+     * Tuning is performed at best effort: the store SHALL update all supported configuration at
+     * best effort (unless configured otherwise) and skip unsupported ones. Any errors are
+     * communicated in the return value and in |failures|.
+     *
+     * \note Parameter tuning DOES depend on the order of the tuning parameters. E.g. some parameter
+     * update may allow some subsequent parameter update.
+     *
+     * This method MUST be "non-blocking" and return within 1ms.
+     *
+     * \param params          a list of parameter updates. These will be updated to the actual
+     *                      parameter values after the updates (this is because tuning is performed
+     *                      at best effort).
+     *                      \todo params that could not be updated are not marked here, so are
+     *                      confusing - are they "existing" values or intended to be configured
+     *                      values?
+     * \param failures        a list of parameter failures
+     *
+     * \retval C2_OK        all parameters could be updated successfully
+     * \retval C2_BAD_INDEX all supported parameters could be updated successfully, but some
+     *                      parameters were not supported
+     * \retval C2_BAD_VALUE some supported parameters could not be updated successfully because
+     *                      they contained unsupported values. These are returned in |failures|.
+     * \retval C2_NO_MEMORY some supported parameters could not be updated successfully because
+     *                      they contained unsupported values, but could not allocate a failure
+     *                      object for them.
+     * \retval C2_CORRUPTED some unknown error prevented the update of the parameters
+     *                      (unexpected)
+     */
+    virtual status_t config_nb(
+            const std::vector<C2Param* const> &params,
+            std::list<std::unique_ptr<C2SettingResult>>* const failures) = 0;
+
+    virtual ~C2ComponentStore() = default;
+};
+
+// ================================================================================================
+
+/// @}
+
+}  // namespace android
+
+#endif  // C2COMPONENT_H_
diff --git a/media/libstagefright/codec2/include/C2Config.h b/media/libstagefright/codec2/include/C2Config.h
new file mode 100644
index 0000000..30e9193
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2Config.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2CONFIG_H_
+#define C2CONFIG_H_
+
+#include <C2ParamDef.h>
+
+namespace android {
+
+/// \defgroup config Component configuration
+/// @{
+
+#ifndef DEFINE_C2_ENUM_VALUE_AUTO_HELPER
+#define DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, ...)
+#define DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, type, names, ...)
+#endif
+
+#define C2ENUM(name, type, ...) \
+enum name : type { __VA_ARGS__ }; \
+DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, NULL, __VA_ARGS__)
+
+#define C2ENUM_CUSTOM_PREFIX(name, type, prefix, ...) \
+enum name : type { __VA_ARGS__ }; \
+DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, __VA_ARGS__)
+
+#define C2ENUM_CUSTOM_NAMES(name, type, names, ...) \
+enum name : type { __VA_ARGS__ }; \
+DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, type, names, __VA_ARGS__)
+
+enum C2ParamIndexKind : uint32_t {
+    /// domain
+    kParamIndexDomain,
+
+    /// configuration descriptors
+    kParamIndexSupportedParams,
+    kParamIndexRequiredParams,
+    kParamIndexReadOnlyParams,
+    kParamIndexRequestedInfos,
+
+    /// latency
+    kParamIndexLatency,
+
+    // generic time behavior
+    kParamIndexTemporal,
+
+    /// port configuration
+    kParamIndexMime,
+    kParamIndexStreamCount,
+    kParamIndexFormat,
+
+    // video info
+
+    kParamIndexStructStart = 0x1,
+    kParamIndexVideoSize,
+    kParamIndexMaxVideoSizeHint,
+
+    kParamIndexParamStart = 0x800,
+};
+
+C2ENUM(C2DomainKind, int32_t,
+    C2DomainVideo,
+    C2DomainAudio,
+    C2DomainOther = C2DomainAudio + 1
+);
+
+// read-only
+
+typedef C2GlobalParam<C2Info, C2SimpleValueStruct<C2DomainKind>, kParamIndexDomain> C2ComponentDomainInfo;
+// typedef C2GlobalParam<C2Info, C2Uint32Value, kParamIndexDomain> C2ComponentDomainInfo;
+//DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<C2DomainKind>, { C2FIELD(mValue, "value") });
+
+// read-only
+typedef C2GlobalParam<C2Info, C2Uint32Array, kParamIndexSupportedParams> C2SupportedParamsInfo;
+
+/// \todo do we define it as a param?
+// read-only
+typedef C2GlobalParam<C2Info, C2Uint32Array, kParamIndexRequiredParams> C2RequiredParamsInfo;
+
+// read-only
+typedef C2GlobalParam<C2Info, C2Uint32Array, kParamIndexReadOnlyParams> C2ReadOnlyParamsInfo;
+
+// read-only
+typedef C2GlobalParam<C2Info, C2Uint32Array, kParamIndexRequestedInfos> C2RequestedInfosInfo;
+
+// read-only
+//typedef C2GlobalParam<C2Info, C2Uint32Value, kParamIndexRequestedInfos> C2RequestedInfosInfo;
+
+/// latency
+
+typedef C2PortParam<C2Info, C2Uint32Value, kParamIndexLatency> C2PortLatencyInfo;
+
+typedef C2GlobalParam<C2Info, C2Uint32Value, kParamIndexLatency> C2ComponentLatencyInfo;
+
+/// \todo
+typedef C2GlobalParam<C2Info, C2Uint32Value, kParamIndexTemporal> C2ComponentTemporalInfo;
+
+/// port configuration
+
+typedef C2PortParam<C2Tuning, C2StringValue, kParamIndexMime> C2PortMimeConfig;
+
+typedef C2PortParam<C2Tuning, C2Uint32Value, kParamIndexStreamCount> C2PortStreamCountConfig;
+
+typedef C2StreamParam<C2Tuning, C2StringValue, kParamIndexMime> C2StreamMimeConfig;
+
+C2ENUM(C2FormatKind, uint32_t,
+    C2FormatCompressed,
+    C2FormatAudio = 1,
+    C2FormatVideo = 4,
+)
+
+typedef C2StreamParam<C2Tuning, C2Uint32Value, kParamIndexFormat> C2StreamFormatConfig;
+
+/*
+   Component description fields:
+
+// format (video/compressed/audio/other-do we need other?) per stream
+
+// likely some of these are exposed as separate settings:
+
+struct C2BaseTuning {
+    // latency characteristics
+    uint32_t latency;
+    bool temporal;               // seems this only makes sense if latency is 1..., so this could be captured as latency = 0
+    uint32_t delay;
+
+    uint32_t numInputStreams;    // RW? - or suggestion only: RO
+    uint32_t numOutputStreams;   // RW
+                                 //
+    // refs characteristics (per stream?)
+    uint32_t maxInputRefs;       // RO
+    uint32_t maxOutputRefs;      // RO
+    uint32_t maxInputMemory;     // RO - max time refs are held for
+    uint32_t maxOutputMemory;    // RO
+
+    // per stream
+    bool compressed;
+    // format... video/compressed/audio/other?
+    // actual "audio/video" format type
+    uint32_t width/height? is this needed, or just queue...
+    // mime...
+};
+*/
+
+
+
+
+
+
+// overall component
+//   => C: domain: audio or video
+//   => C: kind: decoder, encoder or filter
+//   => "mime" class
+
+//   => C: temporal (bool) => does this depend on ordering?
+//   => I: latency
+//   => I: history max duration...
+//   => I: history max frames kept...
+//   => I: reordering depth
+//   => I: frc (bool) (perhaps ratio?)
+//   => I: current frc
+
+//   - pause
+//   => last frame 'number' processed
+//   => current frame 'number' processed
+//   => invalid settings =>[]
+
+// video decoder configuration:                                 // audio
+//   - encoding                                                 // -encoding
+//   - hint: max width/height                                   // -hint: sample rate, channels
+//   - hint: profile/level                                      // -hint: tools used
+//   - hint: framerate (bitrate?)                               // -hint: bitrate
+//   - default: color space (from container)
+//   - hint: color format                                       // -hint: pcm-encoding
+//   - hint: # of views (e.g. MVC)                              // -hint?: channel groups
+//   - default: HDR static info (from container)                // -hint?: channel mappings
+//   - hint: rotation (e.g. for allocator)
+
+// => # of streams required and their formats? (setting?)
+// => # of streams produced and their formats? (tuning)
+
+// => output
+//   - # of views                                               // -channel groups && channel mappings
+//   - width/height/crop/color format/color space/HDR static info (from buffers)
+//     (as required by the allocator & framework)
+//   - SEI (or equivalent) <= [port]
+//     - CC
+//   - reference info
+
+// video encoder configurations
+//   - encoding                                                 // - encoding
+//   - hint: width/height                                       // - hint: sample rate, channels
+//   - hint: frame rate
+//   - hint: max width/height (? does this differ from width/height?)
+//   - # of input (e.g. MVC)                                    // - hint: # groups and mappings
+//   - # of output (e.g. SVC) => bitrates/width/height/framerates? per stream
+//   - hint: profile/level                                      // - hint: profile/level
+//   - HDR static info + (info: HDR)
+//   - color space
+//   - hint: color format?                                      // - hint: pcm encoding
+//   - SEI
+//     - CC
+//   - reference directive
+//   - hint: bitrate (or quality)                               // - hint: bitrate/quality
+//   - optional: codec-specific parameters                      // - optional: csd
+
+// => output                                                    // => output
+//   - layers per stream?                                       // E-AC3?... DTS?...Dolby-Vision?
+//   - reference info
+
+
+// RM:
+//   - need SPS for full knowledge => component should return max. (component can use less)
+//   - critical parameters? (interlaced? profile? level?)
+
+struct C2VideoSizeStruct {
+    int32_t mWidth;     ///< video width
+    int32_t mHeight;    ///< video height
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(VideoSize)
+    C2FIELD(mWidth, "width")
+    C2FIELD(mHeight, "height")
+};
+
+// video size for video decoder [OUT]
+typedef C2StreamParam<C2Info, C2VideoSizeStruct> C2VideoSizeStreamInfo;
+
+// max video size for video decoder [IN]
+typedef C2PortParam<C2Setting, C2VideoSizeStruct, kParamIndexMaxVideoSizeHint> C2MaxVideoSizeHintPortSetting;
+
+// video encoder size [IN]
+typedef C2StreamParam<C2Tuning, C2VideoSizeStruct> C2VideoSizeStreamTuning;
+
+/// @}
+
+} // namespace android
+
+#endif
diff --git a/media/libstagefright/codec2/include/C2Param.h b/media/libstagefright/codec2/include/C2Param.h
new file mode 100644
index 0000000..fd43061
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2Param.h
@@ -0,0 +1,1171 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2PARAM_H_
+#define C2PARAM_H_
+
+#include <C2.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <list>
+#include <string>
+#include <type_traits>
+
+#define C2_PACK __attribute__((packed))
+
+namespace android {
+
+/// \addtogroup Parameters
+/// @{
+
+/// \defgroup internal Internal helpers.
+
+/*!
+ * \file
+ * PARAMETERS: SETTINGs, TUNINGs, and INFOs
+ * ===
+ *
+ * These represent miscellaneous control and metadata information and are likely copied into
+ * kernel space. Therefore, these are C-like structures designed to carry just a small amount of
+ * information. We are using C++ to be able to add constructors, as well as non-virtual and class
+ * methods.
+ *
+ * ==Specification details:
+ *
+ * Restrictions:
+ *   - must be POD struct, e.g. no vtable (no virtual destructor)
+ *   - must have the same size in 64-bit and 32-bit mode (no size_t)
+ *   - as such, no pointer members
+ *
+ * Behavior:
+ * - Params can be global (not related to input or output), related to input or output,
+ *   or related to an input/output stream.
+ * - All params are queried/set using a unique param index, which incorporates a potential stream
+ *   index and/or port.
+ * - Querying (supported) params MUST never fail.
+ * - All params MUST have default values.
+ * - If some fields have "unsupported" or "invalid" values during setting, this SHOULD be
+ *   communicated to the app.
+ *   a) Ideally, this should be avoided.  When setting parameters, in general, component should do
+ *     "best effort" to apply all settings. It should change "invalid/unsupported" values to the
+ *     nearest supported values.
+ *   - This is communicated to the client by changing the source values in tune()/
+ *     configure().
+ *   b) If falling back to a supported value is absolutely impossible, the component SHALL return
+ *     an error for the specific setting, but should continue to apply other settings.
+ *     TODO: this currently may result in unintended results.
+ *
+ * **NOTE:** unlike OMX, params are not versioned. Instead, a new struct with new base index
+ * SHALL be added as new versions are required.
+ *
+ * The proper subtype (Setting, Info or Param) is incorporated into the class type. Define structs
+ * to define multiple subtyped versions of related parameters.
+ *
+ * ==Implementation details:
+ *
+ * - Use macros to define parameters
+ * - All parameters must have a default constructor
+ *   - This is only used for instantiating the class in source (e.g. will not be used
+ *     when building a parameter by the framework from key/value pairs.)
+ */
+
+/// \ingroup internal
+struct _C2ParamManipulator;
+
+/**
+ * Parameter base class.
+ */
+struct C2Param {
+    // param index encompasses the following:
+    //
+    // - type (setting, tuning, info, struct)
+    // - vendor extension flag
+    // - flexible parameter flag
+    // - direction (global, input, output)
+    // - stream flag
+    // - stream ID (usually 0)
+    //
+    // layout:
+    //
+    //      +------+-----+---+------+--------+----|------+--------------+
+    //      | kind | dir | - |stream|streamID|flex|vendor|  base index  |
+    //      +------+-----+---+------+--------+----+------+--------------+
+    //  bit: 31..30 29.28       25   24 .. 17  16    15   14    ..     0
+    //
+public:
+    /**
+     * C2Param kinds, usable as bitmaps.
+     */
+    enum Kind : uint32_t {
+        NONE    = 0,
+        STRUCT  = (1 << 0),
+        INFO    = (1 << 1),
+        SETTING = (1 << 2),
+        TUNING  = (1 << 3) | SETTING, // tunings are settings
+    };
+
+    /**
+     * base index (including the vendor extension bit) is a global index for
+     * C2 parameter structs. (e.g. the same indices cannot be reused for different
+     * structs for different components).
+     */
+    struct BaseIndex {
+    protected:
+        enum : uint32_t {
+            kTypeMask      = 0xC0000000,
+            kTypeStruct    = 0x00000000,
+            kTypeTuning    = 0x40000000,
+            kTypeSetting   = 0x80000000,
+            kTypeInfo      = 0xC0000000,
+
+            kDirMask       = 0x30000000,
+            kDirGlobal     = 0x20000000,
+            kDirUndefined  = 0x30000000, // MUST have all bits set
+            kDirInput      = 0x00000000,
+            kDirOutput     = 0x10000000,
+
+            kStreamFlag    = 0x02000000,
+            kStreamIdMask  = 0x01FE0000,
+            kStreamIdShift = 17,
+            kStreamIdMax   = kStreamIdMask >> kStreamIdShift,
+            kStreamMask    = kStreamFlag | kStreamIdMask,
+
+            kFlexibleFlag  = 0x00010000,
+            kVendorFlag    = 0x00008000,
+            kParamMask     = 0x0000FFFF,
+            kBaseMask      = kParamMask | kFlexibleFlag,
+        };
+
+    public:
+        enum : uint32_t {
+            kVendorStart = kVendorFlag, ///< vendor structs SHALL start after this
+            _kFlexibleFlag = kFlexibleFlag, // TODO: this is only needed for testing
+        };
+
+        /// constructor/conversion from uint32_t
+        inline BaseIndex(uint32_t index) : mIndex(index) { }
+
+        // no conversion from uint64_t
+        inline BaseIndex(uint64_t index) = delete;
+
+        /// returns true iff this is a vendor extension parameter
+        inline bool isVendor() const { return mIndex & kVendorFlag; }
+
+        /// returns true iff this is a flexible parameter (with variable size)
+        inline bool isFlexible() const { return mIndex & kFlexibleFlag; }
+
+        /// returns the base type: the index for the underlying struct
+        inline unsigned int baseIndex() const { return mIndex & kBaseMask; }
+
+        /// returns the param index for the underlying struct
+        inline unsigned int paramIndex() const { return mIndex & kParamMask; }
+
+        DEFINE_FIELD_BASED_COMPARISON_OPERATORS(BaseIndex, mIndex)
+
+    protected:
+        uint32_t mIndex;
+    };
+
+    /**
+     * type encompasses the parameter kind (tuning, setting, info), whether the
+     * parameter is global, input or output, and whether it is for a stream.
+     */
+    struct Type : public BaseIndex {
+        /// returns true iff this is a global parameter (not for input nor output)
+        inline bool isGlobal() const { return (mIndex & kDirMask) == kDirGlobal; }
+        /// returns true iff this is an input or input stream parameter
+        inline bool forInput() const { return (mIndex & kDirMask) == kDirInput; }
+        /// returns true iff this is an output or output stream parameter
+        inline bool forOutput() const { return (mIndex & kDirMask) == kDirOutput; }
+
+        /// returns true iff this is a stream parameter
+        inline bool forStream() const { return mIndex & kStreamFlag; }
+        /// returns true iff this is a port (input or output) parameter
+        inline bool forPort() const   { return !forStream() && !isGlobal(); }
+
+        /// returns the parameter type: the parameter index without the stream ID
+        inline uint32_t type() const { return mIndex & (~kStreamIdMask); }
+
+        /// return the kind of this param
+        inline Kind kind() const {
+            switch (mIndex & kTypeMask) {
+                case kTypeStruct: return STRUCT;
+                case kTypeInfo: return INFO;
+                case kTypeSetting: return SETTING;
+                case kTypeTuning: return TUNING;
+                default: return NONE; // should not happen
+            }
+        }
+
+        /// constructor/conversion from uint32_t
+        inline Type(uint32_t index) : BaseIndex(index) { }
+
+        // no conversion from uint64_t
+        inline Type(uint64_t index) = delete;
+
+    private:
+        friend struct C2Param;   // for setPort()
+        friend struct C2Tuning;  // for kTypeTuning
+        friend struct C2Setting; // for kTypeSetting
+        friend struct C2Info;    // for kTypeInfo
+        // for kDirGlobal
+        template<typename T, typename S, int I, class F> friend struct C2GlobalParam;
+        template<typename T, typename S, int I, class F> friend struct C2PortParam;   // for kDir*
+        template<typename T, typename S, int I, class F> friend struct C2StreamParam; // for kDir*
+        friend struct _C2ParamInspector; // for testing
+
+        /**
+         * Sets the port/stream direction.
+         * @return true on success, false if could not set direction (e.g. it is global param).
+         */
+        inline bool setPort(bool output) {
+            if (isGlobal()) {
+                return false;
+            } else {
+                mIndex = (mIndex & ~kDirMask) | (output ? kDirOutput : kDirInput);
+                return true;
+            }
+        }
+    };
+
+    /**
+     * index encompasses all remaining information: basically the stream ID.
+     */
+    struct Index : public Type {
+        /// returns the index as uint32_t
+        inline operator uint32_t() const { return mIndex; }
+
+        /// constructor/conversion from uint32_t
+        inline Index(uint32_t index) : Type(index) { }
+
+        // no conversion from uint64_t
+        inline Index(uint64_t index) = delete;
+
+        /// returns the stream ID or ~0 if not a stream
+        inline unsigned stream() const {
+            return forStream() ? rawStream() : ~0U;
+        }
+
+    private:
+        friend struct C2Param;           // for setStream, makeStreamId, isValid
+        friend struct _C2ParamInspector; // for testing
+
+        /**
+         * @return true if the type is valid, e.g. direction is not undefined AND
+         * stream is 0 if not a stream param.
+         */
+        inline bool isValid() const {
+            // there is no Type::isValid (even though some of this check could be
+            // performed on types) as this is only used on index...
+            return (forStream() ? rawStream() < kStreamIdMax : rawStream() == 0)
+                    && (mIndex & kDirMask) != kDirUndefined;
+        }
+
+        /// returns the raw stream ID field
+        inline unsigned rawStream() const {
+            return (mIndex & kStreamIdMask) >> kStreamIdShift;
+        }
+
+        /// returns the streamId bitfield for a given |stream|. If stream is invalid,
+        /// returns an invalid bitfield.
+        inline static uint32_t makeStreamId(unsigned stream) {
+            // saturate stream ID (max value is invalid)
+            if (stream > kStreamIdMax) {
+                stream = kStreamIdMax;
+            }
+            return (stream << kStreamIdShift) & kStreamIdMask;
+        }
+
+        /**
+         * Sets the stream index.
+         * \return true on success, false if could not set index (e.g. not a stream param).
+         */
+        inline bool setStream(unsigned stream) {
+            if (forStream()) {
+                mIndex = (mIndex & ~kStreamIdMask) | makeStreamId(stream);
+                return this->stream() < kStreamIdMax;
+            }
+            return false;
+        }
+    };
+
+public:
+    // public getters for Index methods
+
+    /// returns true iff this is a vendor extension parameter
+    inline bool isVendor() const { return _mIndex.isVendor(); }
+    /// returns true iff this is a flexible parameter
+    inline bool isFlexible() const { return _mIndex.isFlexible(); }
+    /// returns true iff this is a global parameter (not for input nor output)
+    inline bool isGlobal() const { return _mIndex.isGlobal(); }
+    /// returns true iff this is an input or input stream parameter
+    inline bool forInput() const { return _mIndex.forInput(); }
+    /// returns true iff this is an output or output stream parameter
+    inline bool forOutput() const { return _mIndex.forOutput(); }
+
+    /// returns true iff this is a stream parameter
+    inline bool forStream() const { return _mIndex.forStream(); }
+    /// returns true iff this is a port (input or output) parameter
+    inline bool forPort() const   { return _mIndex.forPort(); }
+
+    /// returns the stream ID or ~0 if not a stream
+    inline unsigned stream() const { return _mIndex.stream(); }
+
+    /// returns the parameter type: the parameter index without the stream ID
+    inline uint32_t type() const { return _mIndex.type(); }
+
+    /// returns the kind of this parameter
+    inline Kind kind() const { return _mIndex.kind(); }
+
+    /// returns the size of the parameter or 0 if the parameter is invalid
+    inline size_t size() const { return _mSize; }
+
+    /// returns true iff the parameter is valid
+    inline operator bool() const { return _mIndex.isValid() && _mSize > 0; }
+
+    /// returns true iff the parameter is invalid
+    inline bool operator!() const { return !operator bool(); }
+
+    // equality is done by memcmp (use equals() to prevent any overread)
+    inline bool operator==(const C2Param &o) const {
+        return equals(o) && memcmp(this, &o, _mSize) == 0;
+    }
+    inline bool operator!=(const C2Param &o) const { return !operator==(o); }
+
+    /// safe(r) type cast from pointer and size
+    inline static C2Param* From(void *addr, size_t len) {
+        // _mSize must fit into size
+        if (len < sizeof(_mSize) + offsetof(C2Param, _mSize)) {
+            return nullptr;
+        }
+        // _mSize must match length
+        C2Param *param = (C2Param*)addr;
+        if (param->_mSize != len) {
+            return nullptr;
+        }
+        return param;
+    }
+
+#if 0
+    template<typename P, class=decltype(C2Param(P()))>
+    P *As() { return P::From(this); }
+    template<typename P>
+    const P *As() const { return const_cast<const P*>(P::From(const_cast<C2Param*>(this))); }
+#endif
+
+protected:
+    /// sets the stream field. Returns true iff successful.
+    inline bool setStream(unsigned stream) {
+        return _mIndex.setStream(stream);
+    }
+
+    /// sets the port (direction). Returns true iff successful.
+    inline bool setPort(bool output) {
+        return _mIndex.setPort(output);
+    }
+
+public:
+    /// invalidate this parameter. There is no recovery from this call; e.g. parameter
+    /// cannot be 'corrected' to be valid.
+    inline void invalidate() { _mSize = 0; }
+
+    // if other is the same kind of (valid) param as this, copy it into this and return true.
+    // otherwise, do not copy anything, and return false.
+    inline bool updateFrom(const C2Param &other) {
+        if (other._mSize == _mSize && other._mIndex == _mIndex && _mSize > 0) {
+            memcpy(this, &other, _mSize);
+            return true;
+        }
+        return false;
+    }
+
+protected:
+    // returns |o| if it is a null ptr, or if can suitably be a param of given |type| (e.g. has
+    // same type (ignoring stream ID), and size). Otherwise, returns null. If |checkDir| is false,
+    // allow undefined or different direction (e.g. as constructed from C2PortParam() vs.
+    // C2PortParam::input), but still require equivalent type (stream, port or global); otherwise,
+    // return null.
+    inline static const C2Param* ifSuitable(
+            const C2Param* o, size_t size, Type type, size_t flexSize = 0, bool checkDir = true) {
+        if (o == nullptr || o->_mSize < size || (flexSize && ((o->_mSize - size) % flexSize))) {
+            return nullptr;
+        } else if (checkDir) {
+            return o->_mIndex.type() == type.mIndex ? o : nullptr;
+        } else if (o->_mIndex.isGlobal()) {
+            return nullptr;
+        } else {
+            return ((o->_mIndex.type() ^ type.mIndex) & ~Type::kDirMask) ? nullptr : o;
+        }
+    }
+
+    /// base constructor
+    inline C2Param(uint32_t paramSize, Index paramIndex)
+        : _mSize(paramSize),
+          _mIndex(paramIndex) {
+        if (paramSize > sizeof(C2Param)) {
+            memset(this + 1, 0, paramSize - sizeof(C2Param));
+        }
+    }
+
+    /// base constructor with stream set
+    inline C2Param(uint32_t paramSize, Index paramIndex, unsigned stream)
+        : _mSize(paramSize),
+          _mIndex(paramIndex | Index::makeStreamId(stream)) {
+        if (paramSize > sizeof(C2Param)) {
+            memset(this + 1, 0, paramSize - sizeof(C2Param));
+        }
+        if (!forStream()) {
+            invalidate();
+        }
+    }
+
+private:
+    friend struct _C2ParamInspector; // for testing
+
+    /// returns the base type: the index for the underlying struct (for testing
+    /// as this can be gotten by the baseIndex enum)
+    inline uint32_t _baseIndex() const { return _mIndex.baseIndex(); }
+
+    /// returns true iff |o| has the same size and index as this. This performs the
+    /// basic check for equality.
+    inline bool equals(const C2Param &o) const {
+        return _mSize == o._mSize && _mIndex == o._mIndex;
+    }
+
+    uint32_t _mSize;
+    Index _mIndex;
+};
+
+/// \ingroup internal
+/// allow C2Params access to private methods, e.g. constructors
+#define C2PARAM_MAKE_FRIENDS \
+    template<typename U, typename S, int I, class F> friend struct C2GlobalParam; \
+    template<typename U, typename S, int I, class F> friend struct C2PortParam; \
+    template<typename U, typename S, int I, class F> friend struct C2StreamParam; \
+
+/**
+ * Setting base structure for component method signatures. Wrap constructors.
+ */
+struct C2Setting : public C2Param {
+protected:
+    template<typename ...Args>
+    inline C2Setting(const Args(&... args)) : C2Param(args...) { }
+public: // TODO
+    enum : uint32_t { indexFlags = Type::kTypeSetting };
+};
+
+/**
+ * Tuning base structure for component method signatures. Wrap constructors.
+ */
+struct C2Tuning : public C2Setting {
+protected:
+    template<typename ...Args>
+    inline C2Tuning(const Args(&... args)) : C2Setting(args...) { }
+public: // TODO
+    enum : uint32_t { indexFlags = Type::kTypeTuning };
+};
+
+/**
+ * Info base structure for component method signatures. Wrap constructors.
+ */
+struct C2Info : public C2Param {
+protected:
+    template<typename ...Args>
+    inline C2Info(const Args(&... args)) : C2Param(args...) { }
+public: // TODO
+    enum : uint32_t { indexFlags = Type::kTypeInfo };
+};
+
+/**
+ * Structure uniquely specifying a field in an arbitrary structure.
+ *
+ * \note This structure is used differently in C2FieldDescriptor to
+ * identify array fields, such that _mSize is the size of each element. This is
+ * because the field descriptor contains the array-length, and we want to keep
+ * a relevant element size for variable length arrays.
+ */
+struct _C2FieldId {
+//public:
+    /**
+     * Constructor used for C2FieldDescriptor that removes the array extent.
+     *
+     * \param[in] offset pointer to the field in an object at address 0.
+     */
+    template<typename T, class B=typename std::remove_extent<T>::type>
+    inline _C2FieldId(T* offset)
+        : // offset is from "0" so will fit on 32-bits
+          _mOffset((uint32_t)(uintptr_t)(offset)),
+          _mSize(sizeof(B)) { }
+
+    /**
+     * Direct constructor from offset and size.
+     *
+     * \param[in] offset offset of the field.
+     * \param[in] size size of the field.
+     */
+    inline _C2FieldId(size_t offset, size_t size)
+        : _mOffset(offset), _mSize(size) {}
+
+    /**
+     * Constructor used to identify a field in an object.
+     *
+     * \param U[type] pointer to the object that contains this field. This is needed in case the
+     *        field is in an (inherited) base class, in which case T will be that base class.
+     * \param pm[im] member pointer to the field
+     */
+    template<typename R, typename T, typename U, typename B=typename std::remove_extent<R>::type>
+    inline _C2FieldId(U *, R T::* pm)
+        : _mOffset((uint32_t)(uintptr_t)(&(((U*)256)->*pm)) - 256u),
+          _mSize(sizeof(B)) { }
+
+    /**
+     * Constructor used to identify a field in an object.
+     *
+     * \param U[type] pointer to the object that contains this field
+     * \param pm[im] member pointer to the field
+     */
+    template<typename R, typename T, typename B=typename std::remove_extent<R>::type>
+    inline _C2FieldId(R T::* pm)
+        : _mOffset((uint32_t)(uintptr_t)(&(((T*)0)->*pm))),
+          _mSize(sizeof(B)) { }
+
+    inline bool operator==(const _C2FieldId &other) const {
+        return _mOffset == other._mOffset && _mSize == other._mSize;
+    }
+
+    inline bool operator<(const _C2FieldId &other) const {
+        return _mOffset < other._mOffset ||
+            // NOTE: order parent structure before sub field
+            (_mOffset == other._mOffset && _mSize > other._mSize);
+    }
+
+    DEFINE_OTHER_COMPARISON_OPERATORS(_C2FieldId)
+
+#if 0
+    inline uint32_t offset() const { return _mOffset; }
+    inline uint32_t size() const { return _mSize; }
+#endif
+
+#if defined(FRIEND_TEST)
+    friend void PrintTo(const _C2FieldId &d, ::std::ostream*);
+#endif
+
+private:
+    uint32_t _mOffset; // offset of field
+    uint32_t _mSize;   // size of field
+};
+
+/**
+ * Structure uniquely specifying a field in a configuration
+ */
+struct C2ParamField {
+//public:
+    // TODO: fix what this is for T[] (for now size becomes T[1])
+    template<typename S, typename T>
+    inline C2ParamField(S* param, T* offset)
+        : _mIndex(param->index()),
+          _mFieldId(offset) {}
+
+    template<typename R, typename T, typename U>
+    inline C2ParamField(U *p, R T::* pm) : _mIndex(p->type()), _mFieldId(p, pm) { }
+
+    inline bool operator==(const C2ParamField &other) const {
+        return _mIndex == other._mIndex && _mFieldId == other._mFieldId;
+    }
+
+    inline bool operator<(const C2ParamField &other) const {
+        return _mIndex < other._mIndex ||
+            (_mIndex == other._mIndex && _mFieldId < other._mFieldId);
+    }
+
+    DEFINE_OTHER_COMPARISON_OPERATORS(C2ParamField)
+
+private:
+    C2Param::Index _mIndex;
+    _C2FieldId _mFieldId;
+};
+
+/**
+ * A shared (union) representation of numeric values
+ */
+class C2Value {
+public:
+    /// A union of supported primitive types.
+    union Primitive {
+        int32_t  i32;   ///< int32_t value
+        uint32_t u32;   ///< uint32_t value
+        int64_t  i64;   ///< int64_t value
+        uint64_t u64;   ///< uint64_t value
+        float    fp;    ///< float value
+
+        // constructors - implicit
+        Primitive(int32_t value)  : i32(value) { }
+        Primitive(uint32_t value) : u32(value) { }
+        Primitive(int64_t value)  : i64(value) { }
+        Primitive(uint64_t value) : u64(value) { }
+        Primitive(float value)    : fp(value)  { }
+
+        Primitive() : u64(0) { }
+
+    private:
+        friend class C2Value;
+        template<typename T> const T &ref() const;
+    };
+
+    enum Type {
+        NO_INIT,
+        INT32,
+        UINT32,
+        INT64,
+        UINT64,
+        FLOAT,
+    };
+
+    template<typename T> static constexpr Type typeFor();
+
+    // constructors - implicit
+    template<typename T>
+    C2Value(T value)  : mType(typeFor<T>()),  mValue(value) { }
+
+    C2Value() : mType(NO_INIT) { }
+
+    inline Type type() const { return mType; }
+
+    template<typename T>
+    inline bool get(T *value) const {
+        if (mType == typeFor<T>()) {
+            *value = mValue.ref<T>();
+            return true;
+        }
+        return false;
+    }
+
+private:
+    Type mType;
+    Primitive mValue;
+};
+
+template<> const int32_t &C2Value::Primitive::ref<int32_t>() const { return i32; }
+template<> const int64_t &C2Value::Primitive::ref<int64_t>() const { return i64; }
+template<> const uint32_t &C2Value::Primitive::ref<uint32_t>() const { return u32; }
+template<> const uint64_t &C2Value::Primitive::ref<uint64_t>() const { return u64; }
+template<> const float &C2Value::Primitive::ref<float>() const { return fp; }
+
+template<> constexpr C2Value::Type C2Value::typeFor<int32_t>() { return INT32; }
+template<> constexpr C2Value::Type C2Value::typeFor<int64_t>() { return INT64; }
+template<> constexpr C2Value::Type C2Value::typeFor<uint32_t>() { return UINT32; }
+template<> constexpr C2Value::Type C2Value::typeFor<uint64_t>() { return UINT64; }
+template<> constexpr C2Value::Type C2Value::typeFor<float>() { return FLOAT; }
+
+/**
+ * field descriptor. A field is uniquely defined by an index into a parameter.
+ * (Note: Stream-id is not captured as a field.)
+ *
+ * Ordering of fields is by offset. In case of structures, it is depth first,
+ * with a structure taking an index just before and in addition to its members.
+ */
+struct C2FieldDescriptor {
+//public:
+    /** field types and flags
+     * \note: only 32-bit and 64-bit fields are supported (e.g. no boolean, as that
+     * is represented using INT32).
+     */
+    enum Type : uint32_t {
+        // primitive types
+        INT32   = C2Value::INT32,  ///< 32-bit signed integer
+        UINT32  = C2Value::UINT32, ///< 32-bit unsigned integer
+        INT64   = C2Value::INT64,  ///< 64-bit signed integer
+        UINT64  = C2Value::UINT64, ///< 64-bit signed integer
+        FLOAT   = C2Value::FLOAT,  ///< 32-bit floating point
+
+        // array types
+        STRING = 0x100, ///< fixed-size string (POD)
+        BLOB,           ///< blob. Blobs have no sub-elements and can be thought of as byte arrays;
+                        ///< however, bytes cannot be individually addressed by clients.
+
+        // complex types
+        STRUCT_FLAG = 0x10000, ///< structs. Marked with this flag in addition to their baseIndex.
+    };
+
+    typedef std::pair<C2String, C2Value::Primitive> named_value_type;
+    typedef std::vector<const named_value_type> named_values_type;
+    //typedef std::pair<std::vector<C2String>, std::vector<C2Value::Primitive>> named_values_type;
+
+    /**
+     * Template specialization that returns the named values for a type.
+     *
+     * \todo hide from client.
+     *
+     * \return a vector of name-value pairs.
+     */
+    template<typename B>
+    static named_values_type namedValuesFor(const B &);
+
+    inline C2FieldDescriptor(uint32_t type, uint32_t length, C2StringLiteral name, size_t offset, size_t size)
+        : _mType((Type)type), _mLength(length), _mName(name), _mFieldId(offset, size) { }
+
+    template<typename T, class B=typename std::remove_extent<T>::type>
+    inline C2FieldDescriptor(const T* offset, const char *name)
+        : _mType(this->getType((B*)nullptr)),
+          _mLength(std::is_array<T>::value ? std::extent<T>::value : 1),
+          _mName(name),
+          _mNamedValues(namedValuesFor(*(B*)0)),
+          _mFieldId(offset) {}
+
+/*
+    template<typename T, typename B=typename std::remove_extent<T>::type>
+    inline C2FieldDescriptor<T, B, false>(T* offset, const char *name)
+        : _mType(this->getType((B*)nullptr)),
+          _mLength(std::is_array<T>::value ? std::extent<T>::value : 1),
+          _mName(name),
+          _mFieldId(offset) {}
+*/
+
+    /// \deprecated
+    template<typename T, typename S, class B=typename std::remove_extent<T>::type>
+    constexpr inline C2FieldDescriptor(S*, T S::* field, const char *name)
+        : _mType(this->getType((B*)nullptr)),
+          _mLength(std::is_array<T>::value ? std::extent<T>::value : 1),
+          _mName(name),
+          _mFieldId(&(((S*)0)->*field)) {}
+
+    /// returns the type of this field
+    inline Type type() const { return _mType; }
+    /// returns the length of the field in case it is an array. Returns 0 for
+    /// T[] arrays, returns 1 for T[1] arrays as well as if the field is not an array.
+    inline size_t length() const { return _mLength; }
+    /// returns the name of the field
+    inline C2StringLiteral name() const { return _mName; }
+
+    const named_values_type &namedValues() const { return _mNamedValues; }
+
+#if defined(FRIEND_TEST)
+    friend void PrintTo(const C2FieldDescriptor &, ::std::ostream*);
+    friend bool operator==(const C2FieldDescriptor &, const C2FieldDescriptor &);
+    FRIEND_TEST(C2ParamTest_ParamFieldList, VerifyStruct);
+#endif
+
+private:
+    const Type _mType;
+    const uint32_t _mLength; // the last member can be arbitrary length if it is T[] array,
+                       // extending to the end of the parameter (this is marked with
+                       // 0). T[0]-s are not fields.
+    const C2StringLiteral _mName;
+    const named_values_type _mNamedValues;
+
+    const _C2FieldId _mFieldId;   // field identifier (offset and size)
+
+    // NOTE: We do not capture default value(s) here as that may depend on the component.
+    // NOTE: We also do not capture bestEffort, as 1) this should be true for most fields,
+    // 2) this is at parameter granularity.
+
+    // type resolution
+    inline static Type getType(int32_t*)  { return INT32; }
+    inline static Type getType(uint32_t*) { return UINT32; }
+    inline static Type getType(int64_t*)  { return INT64; }
+    inline static Type getType(uint64_t*) { return UINT64; }
+    inline static Type getType(float*)    { return FLOAT; }
+    inline static Type getType(char*)     { return STRING; }
+    inline static Type getType(uint8_t*)  { return BLOB; }
+
+    template<typename T,
+             class=typename std::enable_if<std::is_enum<T>::value>::type>
+    inline static Type getType(T*) {
+        typename std::underlying_type<T>::type underlying(0);
+        return getType(&underlying);
+    }
+
+    // verify C2Struct by having a fieldList and a baseIndex.
+    template<typename T,
+             class=decltype(T::baseIndex + 1), class=decltype(T::fieldList)>
+    inline static Type getType(T*) {
+        static_assert(!std::is_base_of<C2Param, T>::value, "cannot use C2Params as fields");
+        return (Type)(T::baseIndex | STRUCT_FLAG);
+    }
+};
+
+#define DEFINE_NO_NAMED_VALUES_FOR(type) \
+template<> inline C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const type &) { \
+    return named_values_type(); \
+}
+
+// We cannot subtype constructor for enumerated types so insted define no named values for
+// non-enumerated integral types.
+DEFINE_NO_NAMED_VALUES_FOR(int32_t)
+DEFINE_NO_NAMED_VALUES_FOR(uint32_t)
+DEFINE_NO_NAMED_VALUES_FOR(int64_t)
+DEFINE_NO_NAMED_VALUES_FOR(uint64_t)
+DEFINE_NO_NAMED_VALUES_FOR(uint8_t)
+DEFINE_NO_NAMED_VALUES_FOR(char)
+DEFINE_NO_NAMED_VALUES_FOR(float)
+
+/**
+ * Describes the fields of a structure.
+ */
+struct C2StructDescriptor {
+public:
+    /// Returns the parameter type
+    inline C2Param::BaseIndex baseIndex() const { return _mType.baseIndex(); }
+
+    // Returns the number of fields in this param (not counting any recursive fields).
+    // Must be at least 1 for valid params.
+    inline size_t numFields() const { return _mFields.size(); }
+
+    // Returns the list of immediate fields (not counting any recursive fields).
+    typedef std::vector<const C2FieldDescriptor>::const_iterator field_iterator;
+    inline field_iterator cbegin() const { return _mFields.cbegin(); }
+    inline field_iterator cend() const { return _mFields.cend(); }
+
+    // only supplying const iterator - but these are needed for range based loops
+    inline field_iterator begin() const { return _mFields.cbegin(); }
+    inline field_iterator end() const { return _mFields.cend(); }
+
+    template<typename T>
+    inline C2StructDescriptor(T*)
+        : C2StructDescriptor(T::baseIndex, T::fieldList) { }
+
+    inline C2StructDescriptor(
+            C2Param::BaseIndex type,
+            std::initializer_list<const C2FieldDescriptor> fields)
+        : _mType(type), _mFields(fields) { }
+
+private:
+    const C2Param::BaseIndex _mType;
+    const std::vector<const C2FieldDescriptor> _mFields;
+};
+
+/**
+ * Describes parameters for a component.
+ */
+struct C2ParamDescriptor {
+public:
+    /**
+     * Returns whether setting this param is required to configure this component.
+     * This can only be true for builtin params for platform-defined components (e.g. video and
+     * audio encoders/decoders, video/audio filters).
+     * For vendor-defined components, it can be true even for vendor-defined params,
+     * but it is not recommended, in case the component becomes platform-defined.
+     */
+    inline bool isRequired() const { return _mIsRequired; }
+
+    /**
+     * Returns whether this parameter is persistent. This is always true for C2Tuning and C2Setting,
+     * but may be false for C2Info. If true, this parameter persists across frames and applies to
+     * the current and subsequent frames. If false, this C2Info parameter only applies to the
+     * current frame and is not assumed to have the same value (or even be present) on subsequent
+     * frames, unless it is specified for those frames.
+     */
+    inline bool isPersistent() const { return _mIsPersistent; }
+
+    /// Returns the name of this param.
+    /// This defaults to the underlying C2Struct's name, but could be altered for a component.
+    inline C2String name() const { return _mName; }
+
+    /// Returns the parameter type
+    /// \todo fix this
+    inline C2Param::Type type() const { return _mType; }
+
+    template<typename T>
+    inline C2ParamDescriptor(bool isRequired, C2StringLiteral name, const T*)
+        : _mIsRequired(isRequired),
+          _mIsPersistent(true),
+          _mName(name),
+          _mType(T::typeIndex) { }
+
+    inline C2ParamDescriptor(
+            bool isRequired, C2StringLiteral name, C2Param::Type type)
+        : _mIsRequired(isRequired),
+          _mIsPersistent(true),
+          _mName(name),
+          _mType(type) { }
+
+private:
+    const bool _mIsRequired;
+    const bool _mIsPersistent;
+    const C2String _mName;
+    const C2Param::Type _mType;
+};
+
+/// \ingroup internal
+/// Define a structure without baseIndex.
+#define DEFINE_C2STRUCT_NO_BASE(name) \
+public: \
+    typedef C2##name##Struct _type; /**< type name shorthand */ \
+    const static std::initializer_list<const C2FieldDescriptor> fieldList; /**< structure fields */
+
+/// Define a structure with matching baseIndex.
+#define DEFINE_C2STRUCT(name) \
+public: \
+    enum : uint32_t { baseIndex = kParamIndex##name }; \
+    DEFINE_C2STRUCT_NO_BASE(name)
+
+/// Define a flexible structure with matching baseIndex.
+#define DEFINE_FLEX_C2STRUCT(name, flexMember) \
+public: \
+    FLEX(C2##name##Struct, flexMember) \
+    enum : uint32_t { baseIndex = kParamIndex##name | C2Param::BaseIndex::_kFlexibleFlag }; \
+    DEFINE_C2STRUCT_NO_BASE(name)
+
+/// \ingroup internal
+/// Describe a structure of a templated structure.
+#define DESCRIBE_TEMPLATED_C2STRUCT(strukt, list) \
+    template<> \
+    const std::initializer_list<const C2FieldDescriptor> strukt::fieldList = list;
+
+/// \deprecated
+/// Describe the fields of a structure using an initializer list.
+#define DESCRIBE_C2STRUCT(name, list) \
+    const std::initializer_list<const C2FieldDescriptor> C2##name##Struct::fieldList = list;
+
+/**
+ * Describe a field of a structure.
+ * These must be in order.
+ *
+ * There are two ways to use this macro:
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  struct C2VideoWidthStruct {
+ *      int32_t mWidth;
+ *      C2VideoWidthStruct() {} // optional default constructor
+ *      C2VideoWidthStruct(int32_t _width) : mWidth(_width) {}
+ *
+ *      DEFINE_AND_DESCRIBE_C2STRUCT(VideoWidth)
+ *      C2FIELD(mWidth, "width")
+ *  };
+ *  ~~~~~~~~~~~~~
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  struct C2VideoWidthStruct {
+ *      int32_t mWidth;
+ *      C2VideoWidthStruct() = default; // optional default constructor
+ *      C2VideoWidthStruct(int32_t _width) : mWidth(_width) {}
+ *
+ *      DEFINE_C2STRUCT(VideoWidth)
+ *  } C2_PACK;
+ *
+ *  DESCRIBE_C2STRUCT(VideoWidth, {
+ *      C2FIELD(mWidth, "width")
+ *  })
+ *  ~~~~~~~~~~~~~
+ *
+ *  For flexible structures (those ending in T[]), use the flexible macros:
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  struct C2VideoFlexWidthsStruct {
+ *      int32_t mWidths[];
+ *      C2VideoFlexWidthsStruct(); // must have a default constructor
+ *
+ *  private:
+ *      // may have private constructors taking number of widths as the first argument
+ *      // This is used by the C2Param factory methods, e.g.
+ *      //   C2VideoFlexWidthsGlobalParam::alloc_unique(size_t, int32_t);
+ *      C2VideoFlexWidthsStruct(size_t flexCount, int32_t value) {
+ *          for (size_t i = 0; i < flexCount; ++i) {
+ *              mWidths[i] = value;
+ *          }
+ *      }
+ *
+ *      // If the last argument is T[N] or std::initializer_list<T>, the flexCount will
+ *      // be automatically calculated and passed by the C2Param factory methods, e.g.
+ *      //   int widths[] = { 1, 2, 3 };
+ *      //   C2VideoFlexWidthsGlobalParam::alloc_unique(widths);
+ *      template<unsigned N>
+ *      C2VideoFlexWidthsStruct(size_t flexCount, const int32_t(&init)[N]) {
+ *          for (size_t i = 0; i < flexCount; ++i) {
+ *              mWidths[i] = init[i];
+ *          }
+ *      }
+ *
+ *      DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(VideoFlexWidths, mWidths)
+ *      C2FIELD(mWidths, "widths")
+ *  };
+ *  ~~~~~~~~~~~~~
+ *
+ *  ~~~~~~~~~~~~~ (.cpp)
+ *  struct C2VideoFlexWidthsStruct {
+ *      int32_t mWidths[];
+ *      C2VideoFlexWidthsStruct(); // must have a default constructor
+ *
+ *      DEFINE_FLEX_C2STRUCT(VideoFlexWidths, mWidths)
+ *  } C2_PACK;
+ *
+ *  DESCRIBE_C2STRUCT(VideoFlexWidths, {
+ *      C2FIELD(mWidths, "widths")
+ *  })
+ *  ~~~~~~~~~~~~~
+ *
+ */
+#define C2FIELD(member, name) \
+  C2FieldDescriptor(&((_type*)(nullptr))->member, name),
+
+/// \deprecated
+#define C2SOLE_FIELD(member, name) \
+  C2FieldDescriptor(&_type::member, name, 0)
+
+/// Define a structure with matching baseIndex and start describing its fields.
+/// This must be at the end of the structure definition.
+#define DEFINE_AND_DESCRIBE_C2STRUCT(name) \
+    DEFINE_C2STRUCT(name) }  C2_PACK; \
+    const std::initializer_list<const C2FieldDescriptor> C2##name##Struct::fieldList = {
+
+/// Define a flexible structure with matching baseIndex and start describing its fields.
+/// This must be at the end of the structure definition.
+#define DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(name, flexMember) \
+    DEFINE_FLEX_C2STRUCT(name, flexMember) } C2_PACK; \
+    const std::initializer_list<const C2FieldDescriptor> C2##name##Struct::fieldList = {
+
+/**
+ * Parameter reflector class.
+ *
+ * This class centralizes the description of parameter structures. This can be shared
+ * by multiple components as describing a parameter does not imply support of that
+ * parameter. However, each supported parameter and any dependent structures within
+ * must be described by the parameter reflector provided by a component.
+ */
+class C2ParamReflector {
+public:
+    /**
+     *  Describes a parameter structure.
+     *
+     *  \param[in] paramIndex the base index of the parameter structure
+     *
+     *  \return the description of the parameter structure
+     *  \retval nullptr if the parameter is not supported by this reflector
+     *
+     *  This methods shall not block and return immediately.
+     *
+     *  \note this class does not take a set of indices because we would then prefer
+     *  to also return any dependent structures, and we don't want this logic to be
+     *  repeated in each reflector. Alternately, this could just return a map of all
+     *  descriptions, but we want to conserve memory if client only wants the description
+     *  of a few indices.
+     */
+    virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::BaseIndex paramIndex) = 0;
+
+protected:
+    virtual ~C2ParamReflector() = default;
+};
+
+/**
+ * A useable supported values for a field.
+ *
+ * This can be either a range or a set of values. The range can be linear or geometric with a
+ * clear minimum and maximum value, and can have an optional step size or geometric ratio. Values
+ * can optionally represent flags.
+ *
+ * \note Do not use flags to represent bitfields. Use individual values or separate fields instead.
+ */
+template<typename T>
+struct C2TypedFieldSupportedValues {
+//public:
+    enum Type {
+        RANGE,      ///< a numeric range that can be continuous or discrete
+        VALUES,     ///< a list of values
+        FLAGS       ///< a list of flags that can be OR-ed
+    };
+
+    Type type;
+
+    struct {
+        T min;
+        T max;
+        T step;
+        T nom;
+        T denom;
+    } range;
+    std::vector<T> values;
+
+    C2TypedFieldSupportedValues(T min, T max, T step = T(std::is_floating_point<T>::value ? 0 : 1))
+        : type(RANGE),
+          range{min, max, step, (T)1, (T)1} { }
+
+    C2TypedFieldSupportedValues(T min, T max, T nom, T den) :
+        type(RANGE),
+        range{min, max, (T)0, nom, den} { }
+
+    C2TypedFieldSupportedValues(bool flags, std::initializer_list<T> list) :
+        type(flags ? FLAGS : VALUES),
+        values(list) {}
+};
+
+/**
+ * Generic supported values for a field.
+ *
+ * This can be either a range or a set of values. The range can be linear or geometric with a
+ * clear minimum and maximum value, and can have an optional step size or geometric ratio. Values
+ * can optionally represent flags.
+ *
+ * \note Do not use flags to represent bitfields. Use individual values or separate fields instead.
+ */
+struct C2FieldSupportedValues {
+//public:
+    enum Type {
+        RANGE,      ///< a numeric range that can be continuous or discrete
+        VALUES,     ///< a list of values
+        FLAGS       ///< a list of flags that can be OR-ed
+    };
+
+    Type type;
+
+    typedef C2Value::Primitive Primitive;
+
+    struct {
+        Primitive min;
+        Primitive max;
+        Primitive step;
+        Primitive nom;
+        Primitive denom;
+    } range;
+    std::vector<Primitive> values;
+
+    template<typename T>
+    C2FieldSupportedValues(T min, T max, T step = T(std::is_floating_point<T>::value ? 0 : 1))
+        : type(RANGE),
+          range{min, max, step, (T)1, (T)1} { }
+
+    template<typename T>
+    C2FieldSupportedValues(T min, T max, T nom, T den) :
+        type(RANGE),
+        range{min, max, (T)0, nom, den} { }
+
+    template<typename T>
+    C2FieldSupportedValues(bool flags, std::initializer_list<T> list)
+        : type(flags ? FLAGS : VALUES),
+          range{(T)0, (T)0, (T)0, (T)0, (T)0} {
+        for(T value : list) {
+            values.emplace_back(value);
+        }
+    }
+
+    template<typename T, typename E=decltype(C2FieldDescriptor::namedValuesFor(*(T*)0))>
+    C2FieldSupportedValues(bool flags, const T*)
+        : type(flags ? FLAGS : VALUES),
+          range{(T)0, (T)0, (T)0, (T)0, (T)0} {
+              C2FieldDescriptor::named_values_type named = C2FieldDescriptor::namedValuesFor(*(T*)0);
+        for (const C2FieldDescriptor::named_value_type &item : named) {
+            values.emplace_back(item.second);
+        }
+    }
+};
+
+/// @}
+
+}  // namespace android
+
+#endif  // C2PARAM_H_
diff --git a/media/libstagefright/codec2/include/C2ParamDef.h b/media/libstagefright/codec2/include/C2ParamDef.h
new file mode 100644
index 0000000..f369617
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2ParamDef.h
@@ -0,0 +1,901 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** \file
+ * Templates used to declare parameters.
+ */
+#ifndef C2PARAM_DEF_H_
+#define C2PARAM_DEF_H_
+
+#include <type_traits>
+
+#include <C2Param.h>
+
+namespace android {
+
+/// \addtogroup Parameters
+/// @{
+
+/* ======================== UTILITY TEMPLATES FOR PARAMETER DEFINITIONS ======================== */
+
+/// \addtogroup internal
+/// @{
+
+/// Helper class that checks if a type has equality and inequality operators.
+struct C2_HIDE _C2Comparable_impl
+{
+    template<typename S, typename=decltype(S() == S())>
+    static std::true_type __testEQ(int);
+    template<typename>
+    static std::false_type __testEQ(...);
+
+    template<typename S, typename=decltype(S() != S())>
+    static std::true_type __testNE(int);
+    template<typename>
+    static std::false_type __testNE(...);
+};
+
+/**
+ * Helper template that returns if a type has equality and inequality operators.
+ *
+ * Use as _C2Comparable<typename S>::value.
+ */
+template<typename S>
+struct C2_HIDE _C2Comparable
+    : public std::integral_constant<bool, decltype(_C2Comparable_impl::__testEQ<S>(0))::value
+                        || decltype(_C2Comparable_impl::__testNE<S>(0))::value> {
+};
+
+///  Helper class that checks if a type has a baseIndex constant.
+struct C2_HIDE _C2BaseIndexHelper_impl
+{
+    template<typename S, int=S::baseIndex>
+    static std::true_type __testBaseIndex(int);
+    template<typename>
+    static std::false_type __testBaseIndex(...);
+};
+
+/// Helper template that verifies a type's baseIndex and creates it if the type does not have one.
+template<typename S, int BaseIndex,
+        bool HasBase=decltype(_C2BaseIndexHelper_impl::__testBaseIndex<S>(0))::value>
+struct C2_HIDE C2BaseIndexOverride {
+    // TODO: what if we allow structs without baseIndex?
+    static_assert(BaseIndex == S::baseIndex, "baseIndex differs from structure");
+};
+
+/// Specialization for types without a baseIndex.
+template<typename S, int BaseIndex>
+struct C2_HIDE C2BaseIndexOverride<S, BaseIndex, false> {
+public:
+    enum : uint32_t {
+        baseIndex = BaseIndex, ///< baseIndex override.
+    };
+};
+
+/// Helper template that adds a baseIndex to a type if it does not have one.
+template<typename S, int BaseIndex>
+struct C2_HIDE C2AddBaseIndex : public S, public C2BaseIndexOverride<S, BaseIndex> {};
+
+/**
+ * \brief Helper class to check struct requirements for parameters.
+ *
+ * Features:
+ *  - verify default constructor, no virtual methods, and no equality operators.
+ *  - expose typeIndex, and non-flex flexSize.
+ */
+template<typename S, int BaseIndex, unsigned TypeIndex>
+struct C2_HIDE C2StructCheck {
+    static_assert(
+            std::is_default_constructible<S>::value, "C2 structure must have default constructor");
+    static_assert(!std::is_polymorphic<S>::value, "C2 structure must not have virtual methods");
+    static_assert(!_C2Comparable<S>::value, "C2 structure must not have operator== or !=");
+
+public:
+    enum : uint32_t {
+        typeIndex = BaseIndex | TypeIndex
+    };
+
+protected:
+    enum : uint32_t {
+        flexSize = 0, // TODO: is this still needed? this may be confusing.
+    };
+};
+
+/// Helper class that checks if a type has an integer flexSize member.
+struct C2_HIDE _C2Flexible_impl {
+    /// specialization for types that have a flexSize member
+    template<typename S, unsigned=S::flexSize>
+    static std::true_type __testFlexSize(int);
+    template<typename>
+    static std::false_type __testFlexSize(...);
+};
+
+/// Helper template that returns if a type has an integer flexSize member.
+template<typename S>
+struct C2_HIDE _C2Flexible
+    : public std::integral_constant<bool, decltype(_C2Flexible_impl::__testFlexSize<S>(0))::value> {
+};
+
+/// Macro to test if a type is flexible (has a flexSize member).
+#define IF_FLEXIBLE(S) ENABLE_IF(_C2Flexible<S>::value)
+/// Shorthand for std::enable_if
+#define ENABLE_IF(cond) typename std::enable_if<cond>::type
+
+/// Helper template that exposes the flexible subtype of a struct.
+template<typename S, typename E=void>
+struct C2_HIDE _C2FlexHelper {
+    typedef void flexType;
+    enum : uint32_t { flexSize = 0 };
+};
+
+/// Specialization for flexible types.
+template<typename S>
+struct C2_HIDE _C2FlexHelper<S,
+        typename std::enable_if<!std::is_void<typename S::flexMemberType>::value>::type> {
+    typedef typename _C2FlexHelper<typename S::flexMemberType>::flexType flexType;
+    enum : uint32_t { flexSize = _C2FlexHelper<typename S::flexMemberType>::flexSize };
+};
+
+/// Specialization for flex arrays.
+template<typename S>
+struct C2_HIDE _C2FlexHelper<S[],
+        typename std::enable_if<std::is_void<typename _C2FlexHelper<S>::flexType>::value>::type> {
+    typedef S flexType;
+    enum : uint32_t { flexSize = sizeof(S) };
+};
+
+/**
+ * \brief Helper class to check flexible struct requirements and add common operations.
+ *
+ * Features:
+ *  - expose baseIndex and fieldList (this is normally inherited from the struct, but flexible
+ *    structs cannot be base classes and thus inherited from)
+ *  - disable copy assignment and construction (TODO: this is already done in the FLEX macro for the
+ *    flexible struct, so may not be needed here)
+ */
+template<typename S, int BaseIndex, unsigned TypeIndex>
+struct C2_HIDE C2FlexStructCheck : public C2StructCheck<S, BaseIndex, TypeIndex> {
+public:
+    enum : uint32_t {
+        /// \hideinitializer
+        baseIndex = BaseIndex | C2Param::BaseIndex::_kFlexibleFlag, ///< flexible struct base-index
+    };
+
+    const static std::initializer_list<const C2FieldDescriptor> fieldList; // TODO assign here
+
+    // default constructor needed because of the disabled copy constructor
+    inline C2FlexStructCheck() = default;
+
+protected:
+    // cannot copy flexible params
+    C2FlexStructCheck(const C2FlexStructCheck<S, BaseIndex, TypeIndex> &) = delete;
+    C2FlexStructCheck& operator= (const C2FlexStructCheck<S, BaseIndex, TypeIndex> &) = delete;
+
+    // constants used for helper methods
+    enum : uint32_t {
+        /// \hideinitializer
+        flexSize = _C2FlexHelper<S>::flexSize, ///< size of flexible type
+        /// \hideinitializer
+        maxSize = (uint32_t)std::min((size_t)UINT32_MAX, SIZE_MAX), // TODO: is this always u32 max?
+        /// \hideinitializer
+        baseSize = sizeof(S) + sizeof(C2Param), ///< size of the base param
+    };
+
+    /// returns the allocated size of this param with flexCount, or 0 if it would overflow.
+    inline static size_t calcSize(size_t flexCount, size_t size = baseSize) {
+        if (flexCount <= (maxSize - size) / S::flexSize) {
+            return size + S::flexSize * flexCount;
+        }
+        return 0;
+    }
+
+    /// dynamic new operator usable for params of type S
+    inline void* operator new(size_t size, size_t flexCount) noexcept {
+        // TODO: assert(size == baseSize);
+        size = calcSize(flexCount, size);
+        if (size > 0) {
+            return ::operator new(size);
+        }
+        return nullptr;
+    }
+};
+
+// TODO: this probably does not work.
+/// Expose fieldList from subClass;
+template<typename S, int BaseIndex, unsigned TypeIndex>
+const std::initializer_list<const C2FieldDescriptor> C2FlexStructCheck<S, BaseIndex, TypeIndex>::fieldList = S::fieldList;
+
+/// Define From() cast operators for params.
+#define DEFINE_CAST_OPERATORS(_type) \
+    inline static _type* From(C2Param *other) { \
+        return (_type*)C2Param::ifSuitable( \
+                other, sizeof(_type),_type::typeIndex, _type::flexSize, \
+                (_type::typeIndex & T::Index::kDirUndefined) != T::Index::kDirUndefined); \
+    } \
+    inline static const _type* From(const C2Param *other) { \
+        return const_cast<const _type*>(From(const_cast<C2Param *>(other))); \
+    } \
+    inline static _type* From(std::nullptr_t) { return nullptr; } \
+
+/**
+ * Define flexible allocators (alloc_shared or alloc_unique) for flexible params.
+ *  - P::alloc_xyz(flexCount, args...): allocate for given flex-count.
+ *  - P::alloc_xyz(args..., T[]): allocate for size of (and with) init array.
+ *  - P::alloc_xyz(T[]): allocate for size of (and with) init array with no other args.
+ *  - P::alloc_xyz(args..., std::initializer_list<T>): allocate for size of (and with) initializer
+ *    list.
+ */
+#define DEFINE_FLEXIBLE_ALLOC(_type, S, ptr) \
+    template<typename ...Args> \
+    inline static std::ptr##_ptr<_type> alloc_##ptr(size_t flexCount, const Args(&... args)) { \
+        return std::ptr##_ptr<_type>(new(flexCount) _type(flexCount, args...)); \
+    } \
+    /* NOTE: unfortunately this is not supported by clang yet */ \
+    template<typename ...Args, typename U=typename S::flexType, unsigned N> \
+    inline static std::ptr##_ptr<_type> alloc_##ptr(const Args(&... args), const U(&init)[N]) { \
+        return std::ptr##_ptr<_type>(new(N) _type(N, args..., init)); \
+    } \
+    /* so for now, specialize for no args */ \
+    template<typename U=typename S::flexType, unsigned N> \
+    inline static std::ptr##_ptr<_type> alloc_##ptr(const U(&init)[N]) { \
+        return std::ptr##_ptr<_type>(new(N) _type(N, init)); \
+    } \
+    template<typename ...Args, typename U=typename S::flexType> \
+    inline static std::ptr##_ptr<_type> alloc_##ptr( \
+            const Args(&... args), const std::initializer_list<U> &init) { \
+        return std::ptr##_ptr<_type>(new(init.size()) _type(init.size(), args..., init)); \
+    } \
+
+/**
+ * Define flexible methods alloc_shared, alloc_unique and flexCount.
+ */
+#define DEFINE_FLEXIBLE_METHODS(_type, S) \
+    DEFINE_FLEXIBLE_ALLOC(_type, S, shared) \
+    DEFINE_FLEXIBLE_ALLOC(_type, S, unique) \
+    inline size_t flexCount() const { \
+        static_assert(sizeof(_type) == _type::baseSize, "incorrect baseSize"); \
+        size_t sz = this->size(); \
+        if (sz >= sizeof(_type)) { \
+            return (sz - sizeof(_type)) / _type::flexSize; \
+        } \
+        return 0; \
+    } \
+
+/// Mark flexible member variable and make structure flexible.
+#define FLEX(cls, m) \
+    C2_DO_NOT_COPY(cls) \
+private: \
+    C2PARAM_MAKE_FRIENDS \
+    /* default constructor with flexCount */ \
+    inline cls(size_t) : cls() {} \
+    /** \if 0 */ \
+    template<typename, typename> friend struct _C2FlexHelper; \
+    typedef decltype(m) flexMemberType; \
+public: \
+    /* constexpr static flexMemberType cls::* flexMember = &cls::m; */ \
+    typedef typename _C2FlexHelper<flexMemberType>::flexType flexType; \
+    static_assert(\
+            !std::is_void<flexType>::value, \
+            "member is not flexible, or a flexible array of a flexible type"); \
+    enum : uint32_t { flexSize = _C2FlexHelper<flexMemberType>::flexSize }; \
+    /** \endif */ \
+
+/// @}
+
+/**
+ * Global-parameter template.
+ *
+ * Base template to define a global setting/tuning or info based on a structure and
+ * an optional BaseIndex. Global parameters are not tied to a port (input or output).
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped
+ * structure can be accessed directly, and constructors and potential public methods are also
+ * wrapped.
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ */
+template<typename T, typename S, int BaseIndex=S::baseIndex, class Flex=void>
+struct C2_HIDE C2GlobalParam : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+        public C2StructCheck<S, BaseIndex, T::indexFlags | T::Type::kDirGlobal> {
+private:
+    typedef C2GlobalParam<T, S, BaseIndex> _type;
+
+public:
+    /// Wrapper around base structure's constructor.
+    template<typename ...Args>
+    inline C2GlobalParam(const Args(&... args)) : T(sizeof(_type), _type::typeIndex), S(args...) { }
+
+    DEFINE_CAST_OPERATORS(_type)
+};
+
+/**
+ * Global-parameter template for flexible structures.
+ *
+ * Base template to define a global setting/tuning or info based on a flexible structure and
+ * an optional BaseIndex. Global parameters are not tied to a port (input or output).
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped flexible structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible
+ * structures can be accessed via the m member variable; however, the constructors of the structure
+ * are wrapped directly. (This is because flexible types cannot be subclassed.)
+ */
+template<typename T, typename S, int BaseIndex>
+struct C2_HIDE C2GlobalParam<T, S, BaseIndex, IF_FLEXIBLE(S)>
+    : public T, public C2FlexStructCheck<S, BaseIndex, T::indexFlags | T::Type::kDirGlobal> {
+private:
+    typedef C2GlobalParam<T, S, BaseIndex> _type;
+
+    /// Wrapper around base structure's constructor.
+    template<typename ...Args>
+    inline C2GlobalParam(size_t flexCount, const Args(&... args))
+        : T(_type::calcSize(flexCount), _type::typeIndex), m(flexCount, args...) { }
+
+public:
+    S m; ///< wrapped flexible structure
+
+    DEFINE_FLEXIBLE_METHODS(_type, S)
+    DEFINE_CAST_OPERATORS(_type)
+};
+
+/**
+ * Port-parameter template.
+ *
+ * Base template to define a port setting/tuning or info based on a structure and
+ * an optional BaseIndex. Port parameters are tied to a port (input or output), but not to a
+ * specific stream.
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped
+ * structure can be accessed directly, and constructors and potential public methods are also
+ * wrapped.
+ *
+ * There are 3 flavors of port parameters: unspecified, input and output. Parameters with
+ * unspecified port expose a setPort method, and add an initial port parameter to the constructor.
+ */
+template<typename T, typename S, int BaseIndex=S::baseIndex, class Flex=void>
+struct C2_HIDE C2PortParam : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+        private C2StructCheck<S, BaseIndex, T::indexFlags | T::Index::kDirUndefined> {
+private:
+    typedef C2PortParam<T, S, BaseIndex> _type;
+
+public:
+    /// Default constructor.
+    inline C2PortParam() : T(sizeof(_type), _type::typeIndex) { }
+    template<typename ...Args>
+    /// Wrapper around base structure's constructor while specifying port/direction.
+    inline C2PortParam(bool _output, const Args(&... args))
+        : T(sizeof(_type), _output ? output::typeIndex : input::typeIndex), S(args...) { }
+    /// Set port/direction.
+    inline void setPort(bool output) { C2Param::setPort(output); }
+
+    DEFINE_CAST_OPERATORS(_type)
+
+    /// Specialization for an input port parameter.
+    struct input : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+            public C2StructCheck<S, BaseIndex, T::indexFlags | T::Index::kDirInput> {
+        /// Wrapper around base structure's constructor.
+        template<typename ...Args>
+        inline input(const Args(&... args)) : T(sizeof(_type), input::typeIndex), S(args...) { }
+
+        DEFINE_CAST_OPERATORS(input)
+
+    };
+
+    /// Specialization for an output port parameter.
+    struct output : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+            public C2StructCheck<S, BaseIndex, T::indexFlags | T::Index::kDirOutput> {
+        /// Wrapper around base structure's constructor.
+        template<typename ...Args>
+        inline output(const Args(&... args)) : T(sizeof(_type), output::typeIndex), S(args...) { }
+
+        DEFINE_CAST_OPERATORS(output)
+    };
+};
+
+/**
+ * Port-parameter template for flexible structures.
+ *
+ * Base template to define a port setting/tuning or info based on a flexible structure and
+ * an optional BaseIndex. Port parameters are tied to a port (input or output), but not to a
+ * specific stream.
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped flexible structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible
+ * structures can be accessed via the m member variable; however, the constructors of the structure
+ * are wrapped directly. (This is because flexible types cannot be subclassed.)
+ *
+ * There are 3 flavors of port parameters: unspecified, input and output. Parameters with
+ * unspecified port expose a setPort method, and add an initial port parameter to the constructor.
+ */
+template<typename T, typename S, int BaseIndex>
+struct C2_HIDE C2PortParam<T, S, BaseIndex, IF_FLEXIBLE(S)>
+    : public T, public C2FlexStructCheck<S, BaseIndex, T::indexFlags | T::Type::kDirUndefined> {
+private:
+    typedef C2PortParam<T, S, BaseIndex> _type;
+
+    /// Default constructor for basic allocation: new(flexCount) P.
+    inline C2PortParam(size_t flexCount) : T(_type::calcSize(flexCount), _type::typeIndex) { }
+    template<typename ...Args>
+    /// Wrapper around base structure's constructor while also specifying port/direction.
+    inline C2PortParam(size_t flexCount, bool _output, const Args(&... args))
+        : T(_type::calcSize(flexCount), _output ? output::typeIndex : input::typeIndex),
+          m(flexCount, args...) { }
+
+public:
+    /// Set port/direction.
+    inline void setPort(bool output) { C2Param::setPort(output); }
+
+    S m; ///< wrapped flexible structure
+
+    DEFINE_FLEXIBLE_METHODS(_type, S)
+    DEFINE_CAST_OPERATORS(_type)
+
+    /// Specialization for an input port parameter.
+    struct input : public T, public C2BaseIndexOverride<S, BaseIndex>,
+            public C2FlexStructCheck<S, BaseIndex, T::indexFlags | T::Index::kDirInput> {
+    private:
+        /// Wrapper around base structure's constructor while also specifying port/direction.
+        template<typename ...Args>
+        inline input(size_t flexCount, const Args(&... args))
+            : T(_type::calcSize(flexCount), input::typeIndex), m(flexCount, args...) { }
+
+    public:
+        S m; ///< wrapped flexible structure
+
+        DEFINE_FLEXIBLE_METHODS(input, S)
+        DEFINE_CAST_OPERATORS(input)
+    };
+
+    /// Specialization for an output port parameter.
+    struct output : public T, public C2BaseIndexOverride<S, BaseIndex>,
+            public C2FlexStructCheck<S, BaseIndex, T::indexFlags | T::Index::kDirOutput> {
+    private:
+        /// Wrapper around base structure's constructor while also specifying port/direction.
+        template<typename ...Args>
+        inline output(size_t flexCount, const Args(&... args))
+            : T(_type::calcSize(flexCount), output::typeIndex), m(flexCount, args...) { }
+
+    public:
+        S m; ///< wrapped flexible structure
+
+        DEFINE_FLEXIBLE_METHODS(output, S)
+        DEFINE_CAST_OPERATORS(output)
+    };
+};
+
+/**
+ * Stream-parameter template.
+ *
+ * Base template to define a stream setting/tuning or info based on a structure and
+ * an optional BaseIndex. Stream parameters are tied to a specific stream on a port (input or
+ * output).
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields of the wrapped
+ * structure can be accessed directly, and constructors and potential public methods are also
+ * wrapped.
+ *
+ * There are 3 flavors of stream parameters: unspecified port, input and output. All of these expose
+ * a setStream method and an extra initial streamID parameter for the constructor. Moreover,
+ * parameters with unspecified port expose a setPort method, and add an additional initial port
+ * parameter to the constructor.
+ */
+template<typename T, typename S, int BaseIndex=S::baseIndex, class Flex=void>
+struct C2_HIDE C2StreamParam : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+        private C2StructCheck<S, BaseIndex,
+                T::indexFlags | T::Index::kStreamFlag | T::Index::kDirUndefined> {
+private:
+    typedef C2StreamParam<T, S, BaseIndex> _type;
+
+public:
+    /// Default constructor. Port/direction and stream-ID is undefined.
+    inline C2StreamParam() : T(sizeof(_type), _type::typeIndex) { }
+    /// Wrapper around base structure's constructor while also specifying port/direction and
+    /// stream-ID.
+    template<typename ...Args>
+    inline C2StreamParam(bool _output, unsigned stream, const Args(&... args))
+        : T(sizeof(_type), _output ? output::typeIndex : input::typeIndex, stream),
+          S(args...) { }
+    /// Set port/direction.
+    inline void setPort(bool output) { C2Param::setPort(output); }
+    /// Set stream-id. \retval true if the stream-id was successfully set.
+    inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+    DEFINE_CAST_OPERATORS(_type)
+
+    /// Specialization for an input stream parameter.
+    struct input : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+            public C2StructCheck<S, BaseIndex,
+                    T::indexFlags | T::Index::kStreamFlag | T::Type::kDirInput> {
+        /// Default constructor. Stream-ID is undefined.
+        inline input() : T(sizeof(_type), input::typeIndex) { }
+        /// Wrapper around base structure's constructor while also specifying stream-ID.
+        template<typename ...Args>
+        inline input(unsigned stream, const Args(&... args))
+            : T(sizeof(_type), input::typeIndex, stream), S(args...) { }
+        /// Set stream-id. \retval true if the stream-id was successfully set.
+        inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+        DEFINE_CAST_OPERATORS(input)
+    };
+
+    /// Specialization for an output stream parameter.
+    struct output : public T, public S, public C2BaseIndexOverride<S, BaseIndex>,
+            public C2StructCheck<S, BaseIndex,
+                    T::indexFlags | T::Index::kStreamFlag | T::Type::kDirOutput> {
+        /// Default constructor. Stream-ID is undefined.
+        inline output() : T(sizeof(_type), output::typeIndex) { }
+        /// Wrapper around base structure's constructor while also specifying stream-ID.
+        template<typename ...Args>
+        inline output(unsigned stream, const Args(&... args))
+            : T(sizeof(_type), output::typeIndex, stream), S(args...) { }
+        /// Set stream-id. \retval true if the stream-id was successfully set.
+        inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+        DEFINE_CAST_OPERATORS(output)
+    };
+};
+
+/**
+ * Stream-parameter template for flexible structures.
+ *
+ * Base template to define a stream setting/tuning or info based on a flexible structure and
+ * an optional BaseIndex. Stream parameters are tied to a specific stream on a port (input or
+ * output).
+ *
+ * \tparam T param type C2Setting, C2Tuning or C2Info
+ * \tparam S wrapped flexible structure
+ * \tparam BaseIndex optional base-index override. Must be specified for common/reused structures.
+ *
+ * Parameters wrap structures by prepending a (parameter) header. The fields and methods of flexible
+ * structures can be accessed via the m member variable; however, the constructors of the structure
+ * are wrapped directly. (This is because flexible types cannot be subclassed.)
+ *
+ * There are 3 flavors of stream parameters: unspecified port, input and output. All of these expose
+ * a setStream method and an extra initial streamID parameter for the constructor. Moreover,
+ * parameters with unspecified port expose a setPort method, and add an additional initial port
+ * parameter to the constructor.
+ */
+template<typename T, typename S, int BaseIndex>
+struct C2_HIDE C2StreamParam<T, S, BaseIndex, IF_FLEXIBLE(S)>
+    : public T, public C2BaseIndexOverride<S, BaseIndex>,
+      private C2FlexStructCheck<S, BaseIndex,
+              T::indexFlags | T::Index::kStreamFlag | T::Index::kDirUndefined> {
+private:
+    typedef C2StreamParam<T, S> _type;
+    /// Default constructor. Port/direction and stream-ID is undefined.
+    inline C2StreamParam(size_t flexCount) : T(_type::calcSize(flexCount), _type::typeIndex, 0u) { }
+    /// Wrapper around base structure's constructor while also specifying port/direction and
+    /// stream-ID.
+    template<typename ...Args>
+    inline C2StreamParam(size_t flexCount, bool _output, unsigned stream, const Args(&... args))
+        : T(_type::calcSize(flexCount), _output ? output::typeIndex : input::typeIndex, stream),
+          m(flexCount, args...) { }
+
+public:
+    S m; ///< wrapped flexible structure
+
+    /// Set port/direction.
+    inline void setPort(bool output) { C2Param::setPort(output); }
+    /// Set stream-id. \retval true if the stream-id was successfully set.
+    inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+    DEFINE_FLEXIBLE_METHODS(_type, S)
+    DEFINE_CAST_OPERATORS(_type)
+
+    /// Specialization for an input stream parameter.
+    struct input : public T, public C2BaseIndexOverride<S, BaseIndex>,
+            public C2FlexStructCheck<S, BaseIndex,
+                    T::indexFlags | T::Index::kStreamFlag | T::Type::kDirInput> {
+    private:
+        /// Default constructor. Stream-ID is undefined.
+        inline input(size_t flexCount) : T(_type::calcSize(flexCount), input::typeIndex) { }
+        /// Wrapper around base structure's constructor while also specifying stream-ID.
+        template<typename ...Args>
+        inline input(size_t flexCount, unsigned stream, const Args(&... args))
+            : T(_type::calcSize(flexCount), input::typeIndex, stream), m(flexCount, args...) { }
+
+    public:
+        S m; ///< wrapped flexible structure
+
+        /// Set stream-id. \retval true if the stream-id was successfully set.
+        inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+        DEFINE_FLEXIBLE_METHODS(input, S)
+        DEFINE_CAST_OPERATORS(input)
+    };
+
+    /// Specialization for an output stream parameter.
+    struct output : public T, public C2BaseIndexOverride<S, BaseIndex>,
+            public C2FlexStructCheck<S, BaseIndex,
+                    T::indexFlags | T::Index::kStreamFlag | T::Type::kDirOutput> {
+    private:
+        /// Default constructor. Stream-ID is undefined.
+        inline output(size_t flexCount) : T(_type::calcSize(flexCount), output::typeIndex) { }
+        /// Wrapper around base structure's constructor while also specifying stream-ID.
+        template<typename ...Args>
+        inline output(size_t flexCount, unsigned stream, const Args(&... args))
+            : T(_type::calcSize(flexCount), output::typeIndex, stream), m(flexCount, args...) { }
+
+    public:
+        S m; ///< wrapped flexible structure
+
+        /// Set stream-id. \retval true if the stream-id was successfully set.
+        inline bool setStream(unsigned stream) { return C2Param::setStream(stream); }
+
+        DEFINE_FLEXIBLE_METHODS(output, S)
+        DEFINE_CAST_OPERATORS(output)
+    };
+};
+
+/* ======================== SIMPLE VALUE PARAMETERS ======================== */
+
+/**
+ * \ingroup internal
+ * A structure template encapsulating a single element with default constructors and no base-index.
+ */
+template<typename T>
+struct C2SimpleValueStruct {
+    T mValue; ///< simple value of the structure
+    // Default constructor.
+    inline C2SimpleValueStruct() = default;
+    // Constructor with an initial value.
+    inline C2SimpleValueStruct(T value) : mValue(value) {}
+    DEFINE_C2STRUCT_NO_BASE(SimpleValue)
+};
+
+// TODO: move this and next to some generic place
+/**
+ * Interface to a block of (mapped) memory containing an array of some type (T).
+ */
+template<typename T>
+struct C2MemoryBlock {
+    /// \returns the number of elements in this block.
+    virtual size_t size() const = 0;
+    /// \returns a const pointer to the start of this block. Care must be taken to not read outside
+    /// the block.
+    virtual const T *data() const = 0; // TODO: should this be friend access only in some C2Memory module?
+    /// \returns a pointer to the start of this block. Care must be taken to not read or write
+    /// outside the block.
+    inline T *data() { return const_cast<T*>(data()); }
+protected:
+    // TODO: for now it should never be deleted as C2MemoryBlock
+    virtual ~C2MemoryBlock() = default;
+};
+
+/**
+ * Interface to a block of memory containing a constant (constexpr) array of some type (T).
+ */
+template<typename T>
+struct C2ConstMemoryBlock : public C2MemoryBlock<T> {
+    virtual const T * data() const { return mData; }
+    virtual size_t size() const { return mSize; }
+
+    /// Constructor.
+    template<unsigned N>
+    inline constexpr C2ConstMemoryBlock(const T(&init)[N]) : mData(init), mSize(N) {}
+
+private:
+    const T *mData;
+    const size_t mSize;
+};
+
+/// \addtogroup internal
+/// @{
+
+/// Helper class to initialize flexible arrays with various initalizers.
+struct _C2ValueArrayHelper {
+    // char[]-s are used as null terminated strings, so the last element is never inited.
+
+    /// Initialize a flexible array using a constexpr memory block.
+    template<typename T>
+    static void init(T(&array)[], size_t arrayLen, const C2MemoryBlock<T> &block) {
+        // reserve last element for terminal 0 for strings
+        if (arrayLen && std::is_same<T, char>::value) {
+            --arrayLen;
+        }
+        if (block.data()) {
+            memcpy(array, block.data(), std::min(arrayLen, block.size()) * sizeof(T));
+        }
+    }
+
+    /// Initialize a flexible array using an initializer list.
+    template<typename T>
+    static void init(T(&array)[], size_t arrayLen, const std::initializer_list<T> &init) {
+        size_t ix = 0;
+        // reserve last element for terminal 0 for strings
+        if (arrayLen && std::is_same<T, char>::value) {
+            --arrayLen;
+        }
+        for (const T &item : init) {
+            if (ix == arrayLen) {
+                break;
+            }
+            array[ix++] = item;
+        }
+    }
+
+    /// Initialize a flexible array using another flexible array.
+    template<typename T, unsigned N>
+    static void init(T(&array)[], size_t arrayLen, const T(&str)[N]) {
+        // reserve last element for terminal 0 for strings
+        if (arrayLen && std::is_same<T, char>::value) {
+            --arrayLen;
+        }
+        if (arrayLen) {
+            strncpy(array, str, std::min(arrayLen, (size_t)N));
+        }
+    }
+};
+
+/**
+ * Specialization for a flexible blob and string arrays. A structure template encapsulating a single
+ * flexible array member with default flexible constructors and no base-index. This type cannot be
+ * constructed on its own as it's size is 0.
+ *
+ * \internal This is different from C2SimpleArrayStruct<T[]> simply because its member has the name
+ * as mValue to reflect this is a single value.
+ */
+template<typename T>
+struct C2SimpleValueStruct<T[]> {
+    static_assert(std::is_same<T, char>::value || std::is_same<T, uint8_t>::value,
+                  "C2SimpleValueStruct<T[]> is only for BLOB or STRING");
+    T mValue[];
+
+    inline C2SimpleValueStruct() = default;
+    DEFINE_C2STRUCT_NO_BASE(SimpleValue)
+    FLEX(C2SimpleValueStruct, mValue)
+
+private:
+    inline C2SimpleValueStruct(size_t flexCount, const C2MemoryBlock<T> &block) {
+        _C2ValueArrayHelper::init(mValue, flexCount, block);
+    }
+
+    inline C2SimpleValueStruct(size_t flexCount, const std::initializer_list<T> &init) {
+        _C2ValueArrayHelper::init(mValue, flexCount, init);
+    }
+
+    template<unsigned N>
+    inline C2SimpleValueStruct(size_t flexCount, const T(&init)[N]) {
+        _C2ValueArrayHelper::init(mValue, flexCount, init);
+    }
+};
+
+/// @}
+
+/**
+ * A structure template encapsulating a single flexible array element of a specific type (T) with
+ * default constructors and no base-index. This type cannot be constructed on its own as it's size
+ * is 0. Instead, it is meant to be used as a parameter, e.g.
+ *
+ *   typedef C2StreamParam<C2Info, C2SimpleArrayStruct<C2MyFancyStruct>,
+ *           kParamIndexMyFancyArrayStreamParam> C2MyFancyArrayStreamInfo;
+ */
+template<typename T>
+struct C2SimpleArrayStruct {
+    static_assert(!std::is_same<T, char>::value && !std::is_same<T, uint8_t>::value,
+                  "use C2SimpleValueStruct<T[]> is for BLOB or STRING");
+
+    T mValues[]; ///< array member
+    /// Default constructor
+    inline C2SimpleArrayStruct() = default;
+    DEFINE_C2STRUCT_NO_BASE(SimpleArray)
+    FLEX(C2SimpleArrayStruct, mValues)
+
+private:
+    /// Construct from a C2MemoryBlock.
+    /// Used only by the flexible parameter allocators (alloc_unique & alloc_shared).
+    inline C2SimpleArrayStruct(size_t flexCount, const C2MemoryBlock<T> &block) {
+        _C2ValueArrayHelper::init(mValues, flexCount, block);
+    }
+
+    /// Construct from an initializer list.
+    /// Used only by the flexible parameter allocators (alloc_unique & alloc_shared).
+    inline C2SimpleArrayStruct(size_t flexCount, const std::initializer_list<T> &init) {
+        _C2ValueArrayHelper::init(mValues, flexCount, init);
+    }
+
+    /// Construct from another flexible array.
+    /// Used only by the flexible parameter allocators (alloc_unique & alloc_shared).
+    template<unsigned N>
+    inline C2SimpleArrayStruct(size_t flexCount, const T(&init)[N]) {
+        _C2ValueArrayHelper::init(mValues, flexCount, init);
+    }
+};
+
+/**
+ * \addtogroup simplevalue Simple value and array structures.
+ * @{
+ *
+ * Simple value structures.
+ *
+ * Structures containing a single simple value. These can be reused to easily define simple
+ * parameters of various types:
+ *
+ *   typedef C2PortParam<C2Tuning, C2Int32Value, kParamIndexMyIntegerPortParam>
+ *           C2MyIntegerPortParamTuning;
+ *
+ * They contain a single member (mValue or mValues) that is described as "value" or "values".
+ */
+/// A 32-bit signed integer parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<int32_t> C2Int32Value;
+/// A 32-bit signed integer array parameter in mValues, described as "values"
+typedef C2SimpleArrayStruct<int32_t> C2Int32Array;
+/// A 32-bit unsigned integer parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<uint32_t> C2Uint32Value;
+/// A 32-bit unsigned integer array parameter in mValues, described as "values"
+typedef C2SimpleArrayStruct<uint32_t> C2Uint32Array;
+/// A 64-bit signed integer parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<int64_t> C2Int64Value;
+/// A 64-bit signed integer array parameter in mValues, described as "values"
+typedef C2SimpleArrayStruct<int64_t> C2Int64Array;
+/// A 64-bit unsigned integer parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<uint64_t> C2Uint64Value;
+/// A 64-bit unsigned integer array parameter in mValues, described as "values"
+typedef C2SimpleArrayStruct<uint64_t> C2Uint64Array;
+/// A float parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<float> C2FloatValue;
+/// A float array parameter in mValues, described as "values"
+typedef C2SimpleArrayStruct<float> C2FloatArray;
+/// A blob flexible parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<uint8_t[]> C2BlobValue;
+/// A string flexible parameter in mValue, described as "value"
+typedef C2SimpleValueStruct<char[]> C2StringValue;
+
+#if 1
+template<typename T>
+const std::initializer_list<const C2FieldDescriptor> C2SimpleValueStruct<T>::fieldList = { C2FIELD(mValue, "value") };
+template<typename T>
+const std::initializer_list<const C2FieldDescriptor> C2SimpleValueStruct<T[]>::fieldList = { C2FIELD(mValue, "value") };
+template<typename T>
+const std::initializer_list<const C2FieldDescriptor> C2SimpleArrayStruct<T>::fieldList = { C2FIELD(mValues, "values") };
+#else
+// This seem to be able to be handled by the template above
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<int32_t>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<uint32_t>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<int64_t>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<uint64_t>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<float>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<uint8_t[]>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleValueStruct<char[]>, { C2FIELD(mValue, "value") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleArrayStruct<int32_t>, { C2FIELD(mValues, "values") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleArrayStruct<uint32_t>, { C2FIELD(mValues, "values") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleArrayStruct<int64_t>, { C2FIELD(mValues, "values") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleArrayStruct<uint64_t>, { C2FIELD(mValues, "values") });
+DESCRIBE_TEMPLATED_C2STRUCT(C2SimpleArrayStruct<float>, { C2FIELD(mValues, "values") });
+#endif
+
+/// @}
+
+/// @}
+
+}  // namespace android
+
+#endif  // C2PARAM_DEF_H_
diff --git a/media/libstagefright/codec2/include/C2Work.h b/media/libstagefright/codec2/include/C2Work.h
new file mode 100644
index 0000000..a42d11a
--- /dev/null
+++ b/media/libstagefright/codec2/include/C2Work.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2WORK_H_
+
+#define C2WORK_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <C2Param.h>
+#include <C2Buffer.h>
+#include <C2Config.h>
+
+#include <memory>
+#include <list>
+#include <vector>
+
+typedef int status_t;
+
+namespace android {
+
+/// \defgroup work Work and data processing
+/// @{
+
+struct C2SettingResult {
+    enum Failure {
+        READ_ONLY,  ///< parameter is read-only and cannot be set
+        MISMATCH,   ///< parameter mismatches input data
+        BAD_VALUE,  ///< parameter does not accept value
+        BAD_TYPE,   ///< parameter is not supported
+        BAD_PORT,   ///< parameter is not supported on the specific port
+        BAD_INDEX,  ///< parameter is not supported on the specific stream
+        CONFLICT,   ///< parameter is in conflict with another setting
+    };
+
+    C2ParamField field;
+    Failure failure;
+    std::unique_ptr<C2FieldSupportedValues> supportedValues; //< if different from normal (e.g. in conflict w/another param or input data)
+    std::list<C2ParamField> conflictingFields;
+};
+
+// ================================================================================================
+//  WORK
+// ================================================================================================
+
+// node_id-s
+typedef uint32_t node_id;
+
+enum flags_t : uint32_t {
+    BUFFERFLAG_CODEC_CONFIG,
+    BUFFERFLAG_DROP_FRAME,
+    BUFFERFLAG_END_OF_STREAM,
+};
+
+enum {
+    kParamIndexWorkOrdinal,
+};
+
+struct C2WorkOrdinalStruct {
+    uint64_t timestamp;
+    uint64_t frame_index;    // submission ordinal on the initial component
+    uint64_t custom_ordinal; // can be given by the component, e.g. decode order
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(WorkOrdinal)
+    C2FIELD(timestamp, "timestamp")
+    C2FIELD(frame_index, "frame-index")
+    C2FIELD(custom_ordinal, "custom-ordinal")
+};
+
+struct C2BufferPack {
+//public:
+    flags_t  flags;
+    C2WorkOrdinalStruct ordinal;
+    std::vector<std::shared_ptr<C2Buffer>> buffers;
+    //< for initial work item, these may also come from the parser - if provided
+    //< for output buffers, these are the responses to requestedInfos
+    std::list<std::unique_ptr<C2Info>>       infos;
+    std::list<std::shared_ptr<C2InfoBuffer>> infoBuffers;
+};
+
+struct C2Worklet {
+//public:
+    // IN
+    node_id component;
+
+    std::list<std::unique_ptr<C2Param>> tunings; //< tunings to be applied before processing this
+                                                 // worklet
+    std::list<C2Param::Type> requestedInfos;
+    std::vector<std::shared_ptr<C2BlockAllocator>> allocators; //< This vector shall be the same size as
+                                                          //< output.buffers.
+
+    // OUT
+    C2BufferPack output;
+    std::list<std::unique_ptr<C2SettingResult>> failures;
+};
+
+/**
+ * This structure holds information about all a single work item.
+ *
+ * This structure shall be passed by the client to the component for the first worklet. As such,
+ * worklets must not be empty. The ownership of this object is passed.
+ *
+ * input:
+ *      The input data to be processed. This is provided by the client with ownership. When the work
+ *      is returned, the input buffer-pack's buffer vector shall contain nullptrs.
+ *
+ * worklets:
+ *      The chain of components and associated allocators, tunings and info requests that the data
+ *      must pass through. If this has more than a single element, the tunnels between successive
+ *      components of the worklet chain must have been (successfully) pre-registered at the time
+ *      the work is submitted. Allocating the output buffers in the worklets is the responsibility
+ *      of each component. Upon work submission, each output buffer-pack shall be an appropriately
+ *      sized vector containing nullptrs. When the work is completed/returned to the client,
+ *
+ * worklets_processed:
+ *      It shall be initialized to 0 by the client when the work is submitted.
+ *      It shall contain the number of worklets that were successfully processed when the work is
+ *      returned. If this is less then the number of worklets, result must not be success.
+ *      It must be in the range of [0, worklets.size()].
+ *
+ * result:
+ *      The final outcome of the work. If 0 when work is returned, it is assumed that all worklets
+ *      have been processed.
+ */
+struct C2Work {
+//public:
+    // pre-chain infos (for portions of a tunneling chain that happend before this work-chain for
+    // this work item - due to framework facilitated (non-tunneled) work-chaining)
+    std::list<std::pair<std::unique_ptr<C2PortMimeConfig>, std::unique_ptr<C2Info>>> preChainInfos;
+    std::list<std::pair<std::unique_ptr<C2PortMimeConfig>, std::unique_ptr<C2Buffer>>> preChainInfoBlobs;
+
+    C2BufferPack input;
+    std::list<std::unique_ptr<C2Worklet>> worklets;
+
+    uint32_t worklets_processed;
+    status_t result;
+};
+
+struct C2WorkOutline {
+//public:
+    C2WorkOrdinalStruct ordinal;
+    std::list<node_id> chain;
+};
+
+/// @}
+
+}  // namespace android
+
+#endif  // C2WORK_H_
diff --git a/media/libstagefright/codec2/tests/Android.mk b/media/libstagefright/codec2/tests/Android.mk
new file mode 100644
index 0000000..49c4253
--- /dev/null
+++ b/media/libstagefright/codec2/tests/Android.mk
@@ -0,0 +1,37 @@
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := codec2_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+	vndk/C2UtilTest.cpp \
+	C2_test.cpp \
+	C2Param_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libstagefright_codec2 \
+	liblog
+
+LOCAL_C_INCLUDES := \
+	frameworks/av/media/libstagefright/codec2/include \
+	frameworks/av/media/libstagefright/codec2/vndk/include \
+	$(TOP)/frameworks/native/include/media/openmax \
+
+LOCAL_CFLAGS += -Werror -Wall -std=c++14
+LOCAL_CLANG := true
+
+include $(BUILD_NATIVE_TEST)
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/media/libstagefright/codec2/tests/C2Param_test.cpp b/media/libstagefright/codec2/tests/C2Param_test.cpp
new file mode 100644
index 0000000..ec82c84
--- /dev/null
+++ b/media/libstagefright/codec2/tests/C2Param_test.cpp
@@ -0,0 +1,2687 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "C2Param_test"
+
+#include <gtest/gtest.h>
+
+#include <util/C2ParamUtils.h>
+#include <C2ParamDef.h>
+
+namespace android {
+
+void PrintTo(const _C2FieldId &id, ::std::ostream* os) {
+    *os << "@" << id._mOffset << "+" << id._mSize;
+}
+
+void PrintTo(const C2FieldDescriptor &fd, ::std::ostream *os) {
+    using FD=C2FieldDescriptor;
+    switch (fd.type()) {
+    case FD::INT32: *os << "i32"; break;
+    case FD::INT64: *os << "i64"; break;
+    case FD::UINT32: *os << "u32"; break;
+    case FD::UINT64: *os << "u64"; break;
+    case FD::FLOAT: *os << "float"; break;
+    case FD::STRING: *os << "char"; break;
+    case FD::BLOB: *os << "u8"; break;
+    default:
+        if (fd.type() & FD::STRUCT_FLAG) {
+            *os << "struct-" << (fd.type() & ~FD::STRUCT_FLAG);
+        } else {
+            *os << "type-" << fd.type();
+        }
+    }
+    *os << " " << fd.name();
+    if (fd.length() > 1) {
+        *os << "[" << fd.length() << "]";
+    } else if (fd.length() == 0) {
+        *os << "[]";
+    }
+    *os << " (";
+    PrintTo(fd._mFieldId, os);
+    *os << "*" << fd.length() << ")";
+}
+
+enum C2ParamIndexType {
+    kParamIndexNumber,
+    kParamIndexNumbers,
+    kParamIndexNumber2,
+    kParamIndexVendorStart = C2Param::BaseIndex::kVendorStart,
+    kParamIndexVendorNumbers,
+};
+
+void ffff(int(*)(int)) {}
+
+/* ============================= STRUCT DECLARATION AND DESCRIPTION ============================= */
+
+typedef C2FieldDescriptor FD;
+
+class C2ParamTest : public ::testing::Test {
+};
+
+class C2ParamTest_ParamFieldList
+        : public ::testing::TestWithParam<std::initializer_list<const C2FieldDescriptor>> {
+};
+
+enum {
+    kParamIndexSize,
+    kParamIndexTestA,
+    kParamIndexTestB,
+    kParamIndexTestFlexS32,
+    kParamIndexTestFlexEndS32,
+    kParamIndexTestFlexS64,
+    kParamIndexTestFlexEndS64,
+    kParamIndexTestFlexSize,
+    kParamIndexTestFlexEndSize,
+};
+
+struct C2SizeStruct {
+    int32_t mNumber;
+    int32_t mHeight;
+    enum : uint32_t { baseIndex = kParamIndexSize };                        // <= needed for C2FieldDescriptor
+    const static std::initializer_list<const C2FieldDescriptor> fieldList;  // <= needed for C2FieldDescriptor
+    const static FD::Type TYPE = (FD::Type)(baseIndex | FD::STRUCT_FLAG);
+};
+
+DEFINE_NO_NAMED_VALUES_FOR(C2SizeStruct)
+
+// Test 1. define a structure without any helper methods
+
+bool operator==(const C2FieldDescriptor &a, const C2FieldDescriptor &b) {
+    return a.type() == b.type()
+            && a.length() == b.length()
+            && strcmp(a.name(), b.name()) == 0
+            && a._mFieldId == b._mFieldId;
+}
+
+struct C2TestStruct_A {
+    int32_t mSigned32;
+    int64_t mSigned64[2];
+    uint32_t mUnsigned32[1];
+    uint64_t mUnsigned64;
+    float mFloat;
+    C2SizeStruct mSize[3];
+    uint8_t mBlob[100];
+    char mString[100];
+    bool mYesNo[100];
+
+    const static std::initializer_list<const C2FieldDescriptor> fieldList;
+    // enum : uint32_t { baseIndex = kParamIndexTest };
+    // typedef C2TestStruct_A _type;
+} __attribute__((packed));
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_A::fieldList =
+    { { FD::INT32,    1, "s32",   0, 4 },
+      { FD::INT64,    2, "s64",   4, 8 },
+      { FD::UINT32,   1, "u32",  20, 4 },
+      { FD::UINT64,   1, "u64",  24, 8 },
+      { FD::FLOAT,    1, "fp",   32, 4 },
+      { C2SizeStruct::TYPE, 3, "size", 36, 8 },
+      { FD::BLOB,   100, "blob", 60, 1 },
+      { FD::STRING, 100, "str", 160, 1 },
+      { FD::BLOB,   100, "y-n", 260, 1 } };
+
+TEST_P(C2ParamTest_ParamFieldList, VerifyStruct) {
+    std::vector<const C2FieldDescriptor> fields = GetParam(), expected = C2TestStruct_A::fieldList;
+
+    // verify first field descriptor
+    EXPECT_EQ(FD::INT32, fields[0].type());
+    EXPECT_STREQ("s32", fields[0].name());
+    EXPECT_EQ(1u, fields[0].length());
+    EXPECT_EQ(_C2FieldId(0, 4), fields[0]._mFieldId);
+
+    EXPECT_EQ(expected[0], fields[0]);
+    EXPECT_EQ(expected[1], fields[1]);
+    EXPECT_EQ(expected[2], fields[2]);
+    EXPECT_EQ(expected[3], fields[3]);
+    EXPECT_EQ(expected[4], fields[4]);
+    EXPECT_EQ(expected[5], fields[5]);
+    EXPECT_EQ(expected[6], fields[6]);
+    EXPECT_EQ(expected[7], fields[7]);
+    for (size_t i = 8; i < fields.size() && i < expected.size(); ++i) {
+        EXPECT_EQ(expected[i], fields[i]);
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(InitializerList, C2ParamTest_ParamFieldList, ::testing::Values(C2TestStruct_A::fieldList));
+
+// define fields using C2FieldDescriptor pointer constructor
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_A_FD_PTR_fieldList =
+    { C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mSigned32,   "s32"),
+      C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mSigned64,   "s64"),
+      C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mUnsigned32, "u32"),
+      C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mUnsigned64, "u64"),
+      C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mFloat,      "fp"),
+      C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mSize,       "size"),
+      C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mBlob,       "blob"),
+      C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mString,     "str"),
+    //  C2FieldDescriptor(&((C2TestStruct_A*)(nullptr))->mYesNo,      "y-n")
+    };
+
+INSTANTIATE_TEST_CASE_P(PointerConstructor, C2ParamTest_ParamFieldList, ::testing::Values(C2TestStruct_A_FD_PTR_fieldList));
+
+// define fields using C2FieldDescriptor member-pointer constructor
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_A_FD_MEM_PTR_fieldList =
+    { C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mSigned32,   "s32"),
+      C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mSigned64,   "s64"),
+      C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mUnsigned32, "u32"),
+      C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mUnsigned64, "u64"),
+      C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mFloat,      "fp"),
+      C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mSize,       "size"),
+      C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mBlob,       "blob"),
+      C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mString,     "str"),
+    //  C2FieldDescriptor((C2TestStruct_A*)0, &C2TestStruct_A::mYesNo,      "y-n")
+    };
+
+INSTANTIATE_TEST_CASE_P(MemberPointerConstructor, C2ParamTest_ParamFieldList, ::testing::Values(C2TestStruct_A_FD_MEM_PTR_fieldList));
+
+// Test 2. define a structure with two-step helper methods
+
+struct C2TestAStruct {
+    int32_t mSigned32;
+    int64_t mSigned64[2];
+    uint32_t mUnsigned32[1];
+    uint64_t mUnsigned64;
+    float mFloat;
+    C2SizeStruct mSize[3];
+    uint8_t mBlob[100];
+    char mString[100];
+    bool mYesNo[100];
+
+private: // test access level
+    DEFINE_C2STRUCT(TestA)
+} C2_PACK;
+
+DESCRIBE_C2STRUCT(TestA, {
+    C2FIELD(mSigned32, "s32")
+    C2FIELD(mSigned64, "s64")
+    C2FIELD(mUnsigned32, "u32")
+    C2FIELD(mUnsigned64, "u64")
+    C2FIELD(mFloat, "fp")
+    C2FIELD(mSize, "size")
+    C2FIELD(mBlob, "blob")
+    C2FIELD(mString, "str")
+    // C2FIELD(mYesNo, "y-n")
+}) // ; optional
+
+INSTANTIATE_TEST_CASE_P(DescribeStruct2Step, C2ParamTest_ParamFieldList, ::testing::Values(C2TestAStruct::fieldList));
+
+// Test 3. define a structure with one-step helper method
+
+struct C2TestBStruct {
+    int32_t mSigned32;
+    int64_t mSigned64[2];
+    uint32_t mUnsigned32[1];
+    uint64_t mUnsigned64;
+    float mFloat;
+    C2SizeStruct mSize[3];
+    uint8_t mBlob[100];
+    char mString[100];
+    bool mYesNo[100];
+
+private: // test access level
+    DEFINE_AND_DESCRIBE_C2STRUCT(TestB)
+
+    C2FIELD(mSigned32, "s32")
+    C2FIELD(mSigned64, "s64")
+    C2FIELD(mUnsigned32, "u32")
+    C2FIELD(mUnsigned64, "u64")
+    C2FIELD(mFloat, "fp")
+    C2FIELD(mSize, "size")
+    C2FIELD(mBlob, "blob")
+    C2FIELD(mString, "str")
+    // C2FIELD(mYesNo, "y-n")
+};
+
+INSTANTIATE_TEST_CASE_P(DescribeStruct1Step, C2ParamTest_ParamFieldList, ::testing::Values(C2TestBStruct::fieldList));
+
+// Test 4. flexible members
+
+template<typename T>
+class C2ParamTest_FlexParamFieldList : public ::testing::Test {
+protected:
+    using Type=FD::Type;
+
+    // static std::initializer_list<std::initializer_list<const C2FieldDescriptor>>
+    static std::vector<std::vector<const C2FieldDescriptor>>
+            GetLists();
+
+    constexpr static Type flexType =
+            std::is_same<T, int32_t>::value ? FD::INT32 :
+            std::is_same<T, int64_t>::value ? FD::INT64 :
+            std::is_same<T, uint32_t>::value ? FD::UINT32 :
+            std::is_same<T, uint64_t>::value ? FD::UINT64 :
+            std::is_same<T, float>::value ? FD::FLOAT :
+            std::is_same<T, uint8_t>::value ? FD::BLOB :
+            std::is_same<T, char>::value ? FD::STRING :
+            std::is_same<T, C2SizeStruct>::value ? C2SizeStruct::TYPE : (Type)0;
+    constexpr static size_t flexSize = sizeof(T);
+};
+
+typedef ::testing::Types<int32_t, int64_t, C2SizeStruct> FlexTypes;
+TYPED_TEST_CASE(C2ParamTest_FlexParamFieldList, FlexTypes);
+
+TYPED_TEST(C2ParamTest_FlexParamFieldList, VerifyStruct) {
+    for (auto a : this->GetLists()) {
+        std::vector<const C2FieldDescriptor> fields = a;
+        if (fields.size() > 1) {
+            EXPECT_EQ(2u, fields.size());
+            EXPECT_EQ(C2FieldDescriptor(FD::INT32, 1, "s32", 0, 4), fields[0]);
+            EXPECT_EQ(C2FieldDescriptor(this->flexType, 0, "flex", 4, this->flexSize),
+                      fields[1]);
+        } else {
+            EXPECT_EQ(1u, fields.size());
+            EXPECT_EQ(C2FieldDescriptor(this->flexType, 0, "flex", 0, this->flexSize),
+                      fields[0]);
+        }
+    }
+}
+
+struct C2TestStruct_FlexS32 {
+    int32_t mFlex[];
+
+    const static std::initializer_list<const C2FieldDescriptor> fieldList;
+    // enum : uint32_t { baseIndex = kParamIndexTestFlex, flexSize = 4 };
+    // typedef C2TestStruct_FlexS32 _type;
+    // typedef int32_t flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexS32::fieldList = {
+    { FD::INT32, 0, "flex", 0, 4 }
+};
+
+struct C2TestStruct_FlexEndS32 {
+    int32_t mSigned32;
+    int32_t mFlex[];
+
+    const static std::initializer_list<const C2FieldDescriptor> fieldList;
+    // enum : uint32_t { baseIndex = kParamIndexTestFlexEnd, flexSize = 4 };
+    // typedef C2TestStruct_FlexEnd _type;
+    // typedef int32_t flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexEndS32::fieldList = {
+    { FD::INT32, 1, "s32", 0, 4 },
+    { FD::INT32, 0, "flex", 4, 4 },
+};
+
+const static std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexEndS32_ptr_fieldList = {
+    C2FieldDescriptor(&((C2TestStruct_FlexEndS32*)0)->mSigned32, "s32"),
+    C2FieldDescriptor(&((C2TestStruct_FlexEndS32*)0)->mFlex, "flex"),
+};
+
+struct C2TestFlexS32Struct {
+    int32_t mFlexSigned32[];
+private: // test access level
+    C2TestFlexS32Struct() {}
+
+    DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(TestFlexS32, mFlexSigned32)
+    C2FIELD(mFlexSigned32, "flex")
+};
+
+struct C2TestFlexEndS32Struct {
+    int32_t mSigned32;
+    int32_t mFlexSigned32[];
+private: // test access level
+    C2TestFlexEndS32Struct() {}
+
+    DEFINE_FLEX_C2STRUCT(TestFlexEndS32, mFlexSigned32)
+} C2_PACK;
+
+DESCRIBE_C2STRUCT(TestFlexEndS32, {
+    C2FIELD(mSigned32, "s32")
+    C2FIELD(mFlexSigned32, "flex")
+}) // ; optional
+
+template<>
+std::vector<std::vector<const C2FieldDescriptor>>
+//std::initializer_list<std::initializer_list<const C2FieldDescriptor>>
+C2ParamTest_FlexParamFieldList<int32_t>::GetLists() {
+    return {
+        C2TestStruct_FlexS32::fieldList,
+        C2TestStruct_FlexEndS32::fieldList,
+        C2TestStruct_FlexEndS32_ptr_fieldList,
+        C2TestFlexS32Struct::fieldList,
+        C2TestFlexEndS32Struct::fieldList,
+    };
+}
+
+struct C2TestStruct_FlexS64 {
+    int64_t mFlexSigned64[];
+
+    const static std::initializer_list<const C2FieldDescriptor> fieldList;
+    // enum : uint32_t { baseIndex = kParamIndexTestFlexS64, flexSize = 8 };
+    // typedef C2TestStruct_FlexS64 _type;
+    // typedef int64_t flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexS64::fieldList = {
+    { FD::INT64, 0, "flex", 0, 8 }
+};
+
+struct C2TestStruct_FlexEndS64 {
+    int32_t mSigned32;
+    int64_t mSigned64Flex[];
+
+    const static std::initializer_list<const C2FieldDescriptor> fieldList;
+    // enum : uint32_t { baseIndex = C2TestStruct_FlexEndS64, flexSize = 8 };
+    // typedef C2TestStruct_FlexEndS64 _type;
+    // typedef int64_t flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexEndS64::fieldList = {
+    { FD::INT32, 1, "s32", 0, 4 },
+    { FD::INT64, 0, "flex", 4, 8 },
+};
+
+struct C2TestFlexS64Struct {
+    int64_t mFlexSigned64[];
+    C2TestFlexS64Struct() {}
+
+    DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(TestFlexS64, mFlexSigned64)
+    C2FIELD(mFlexSigned64, "flex")
+};
+
+struct C2TestFlexEndS64Struct {
+    int32_t mSigned32;
+    int64_t mFlexSigned64[];
+    C2TestFlexEndS64Struct() {}
+
+    DEFINE_FLEX_C2STRUCT(TestFlexEndS64, mFlexSigned64)
+} C2_PACK;
+
+DESCRIBE_C2STRUCT(TestFlexEndS64, {
+    C2FIELD(mSigned32, "s32")
+    C2FIELD(mFlexSigned64, "flex")
+}) // ; optional
+
+template<>
+std::vector<std::vector<const C2FieldDescriptor>>
+//std::initializer_list<std::initializer_list<const C2FieldDescriptor>>
+C2ParamTest_FlexParamFieldList<int64_t>::GetLists() {
+    return {
+        C2TestStruct_FlexS64::fieldList,
+        C2TestStruct_FlexEndS64::fieldList,
+        C2TestFlexS64Struct::fieldList,
+        C2TestFlexEndS64Struct::fieldList,
+    };
+}
+
+struct C2TestStruct_FlexSize {
+    C2SizeStruct mFlexSize[];
+
+    const static std::initializer_list<const C2FieldDescriptor> fieldList;
+    // enum : uint32_t { baseIndex = kParamIndexTestFlexSize, flexSize = 8 };
+    // typedef C2TestStruct_FlexSize _type;
+    // typedef C2SizeStruct flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexSize::fieldList = {
+    { C2SizeStruct::TYPE, 0, "flex", 0, sizeof(C2SizeStruct) }
+};
+
+struct C2TestStruct_FlexEndSize {
+    int32_t mSigned32;
+    C2SizeStruct mSizeFlex[];
+
+    const static std::initializer_list<const C2FieldDescriptor> fieldList;
+    // enum : uint32_t { baseIndex = C2TestStruct_FlexEndSize, flexSize = 8 };
+    // typedef C2TestStruct_FlexEndSize _type;
+    // typedef C2SizeStruct flexType;
+};
+
+const std::initializer_list<const C2FieldDescriptor> C2TestStruct_FlexEndSize::fieldList = {
+    { FD::INT32, 1, "s32", 0, 4 },
+    { C2SizeStruct::TYPE, 0, "flex", 4, sizeof(C2SizeStruct) },
+};
+
+struct C2TestFlexSizeStruct {
+    C2SizeStruct mFlexSize[];
+    C2TestFlexSizeStruct() {}
+
+    DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(TestFlexSize, mFlexSize)
+    C2FIELD(mFlexSize, "flex")
+};
+
+struct C2TestFlexEndSizeStruct {
+    int32_t mSigned32;
+    C2SizeStruct mFlexSize[];
+    C2TestFlexEndSizeStruct() {}
+
+    DEFINE_FLEX_C2STRUCT(TestFlexEndSize, mFlexSize)
+} C2_PACK;
+
+DESCRIBE_C2STRUCT(TestFlexEndSize, {
+    C2FIELD(mSigned32, "s32")
+    C2FIELD(mFlexSize, "flex")
+}) // ; optional
+
+template<>
+std::vector<std::vector<const C2FieldDescriptor>>
+//std::initializer_list<std::initializer_list<const C2FieldDescriptor>>
+C2ParamTest_FlexParamFieldList<C2SizeStruct>::GetLists() {
+    return {
+        C2TestStruct_FlexSize::fieldList,
+        C2TestStruct_FlexEndSize::fieldList,
+        C2TestFlexSizeStruct::fieldList,
+        C2TestFlexEndSizeStruct::fieldList,
+    };
+}
+
+TEST_F(C2ParamTest, FieldId) {
+    // pointer constructor
+    EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId(&((C2TestStruct_A*)0)->mSigned32));
+    EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId(&((C2TestStruct_A*)0)->mSigned64));
+    EXPECT_EQ(_C2FieldId(20, 4), _C2FieldId(&((C2TestStruct_A*)0)->mUnsigned32));
+    EXPECT_EQ(_C2FieldId(24, 8), _C2FieldId(&((C2TestStruct_A*)0)->mUnsigned64));
+    EXPECT_EQ(_C2FieldId(32, 4), _C2FieldId(&((C2TestStruct_A*)0)->mFloat));
+    EXPECT_EQ(_C2FieldId(36, 8), _C2FieldId(&((C2TestStruct_A*)0)->mSize));
+    EXPECT_EQ(_C2FieldId(60, 1), _C2FieldId(&((C2TestStruct_A*)0)->mBlob));
+    EXPECT_EQ(_C2FieldId(160, 1), _C2FieldId(&((C2TestStruct_A*)0)->mString));
+    EXPECT_EQ(_C2FieldId(260, 1), _C2FieldId(&((C2TestStruct_A*)0)->mYesNo));
+
+    EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId(&((C2TestFlexEndSizeStruct*)0)->mSigned32));
+    EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId(&((C2TestFlexEndSizeStruct*)0)->mFlexSize));
+
+    // member pointer constructor
+    EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mSigned32));
+    EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mSigned64));
+    EXPECT_EQ(_C2FieldId(20, 4), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mUnsigned32));
+    EXPECT_EQ(_C2FieldId(24, 8), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mUnsigned64));
+    EXPECT_EQ(_C2FieldId(32, 4), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mFloat));
+    EXPECT_EQ(_C2FieldId(36, 8), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mSize));
+    EXPECT_EQ(_C2FieldId(60, 1), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mBlob));
+    EXPECT_EQ(_C2FieldId(160, 1), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mString));
+    EXPECT_EQ(_C2FieldId(260, 1), _C2FieldId((C2TestStruct_A*)0, &C2TestStruct_A::mYesNo));
+
+    EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId((C2TestFlexEndSizeStruct*)0, &C2TestFlexEndSizeStruct::mSigned32));
+    EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId((C2TestFlexEndSizeStruct*)0, &C2TestFlexEndSizeStruct::mFlexSize));
+
+    // member pointer sans type pointer
+    EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId(&C2TestStruct_A::mSigned32));
+    EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId(&C2TestStruct_A::mSigned64));
+    EXPECT_EQ(_C2FieldId(20, 4), _C2FieldId(&C2TestStruct_A::mUnsigned32));
+    EXPECT_EQ(_C2FieldId(24, 8), _C2FieldId(&C2TestStruct_A::mUnsigned64));
+    EXPECT_EQ(_C2FieldId(32, 4), _C2FieldId(&C2TestStruct_A::mFloat));
+    EXPECT_EQ(_C2FieldId(36, 8), _C2FieldId(&C2TestStruct_A::mSize));
+    EXPECT_EQ(_C2FieldId(60, 1), _C2FieldId(&C2TestStruct_A::mBlob));
+    EXPECT_EQ(_C2FieldId(160, 1), _C2FieldId(&C2TestStruct_A::mString));
+    EXPECT_EQ(_C2FieldId(260, 1), _C2FieldId(&C2TestStruct_A::mYesNo));
+
+    EXPECT_EQ(_C2FieldId(0, 4), _C2FieldId(&C2TestFlexEndSizeStruct::mSigned32));
+    EXPECT_EQ(_C2FieldId(4, 8), _C2FieldId(&C2TestFlexEndSizeStruct::mFlexSize));
+
+    typedef C2GlobalParam<C2Info, C2TestAStruct> C2TestAInfo;
+    typedef C2GlobalParam<C2Info, C2TestFlexEndSizeStruct> C2TestFlexEndSizeInfo;
+
+    // pointer constructor in C2Param
+    EXPECT_EQ(_C2FieldId(8, 4), _C2FieldId(&((C2TestAInfo*)0)->mSigned32));
+    EXPECT_EQ(_C2FieldId(12, 8), _C2FieldId(&((C2TestAInfo*)0)->mSigned64));
+    EXPECT_EQ(_C2FieldId(28, 4), _C2FieldId(&((C2TestAInfo*)0)->mUnsigned32));
+    EXPECT_EQ(_C2FieldId(32, 8), _C2FieldId(&((C2TestAInfo*)0)->mUnsigned64));
+    EXPECT_EQ(_C2FieldId(40, 4), _C2FieldId(&((C2TestAInfo*)0)->mFloat));
+    EXPECT_EQ(_C2FieldId(44, 8), _C2FieldId(&((C2TestAInfo*)0)->mSize));
+    EXPECT_EQ(_C2FieldId(68, 1), _C2FieldId(&((C2TestAInfo*)0)->mBlob));
+    EXPECT_EQ(_C2FieldId(168, 1), _C2FieldId(&((C2TestAInfo*)0)->mString));
+    EXPECT_EQ(_C2FieldId(268, 1), _C2FieldId(&((C2TestAInfo*)0)->mYesNo));
+
+    EXPECT_EQ(_C2FieldId(8, 4), _C2FieldId(&((C2TestFlexEndSizeInfo*)0)->m.mSigned32));
+    EXPECT_EQ(_C2FieldId(12, 8), _C2FieldId(&((C2TestFlexEndSizeInfo*)0)->m.mFlexSize));
+
+    // member pointer in C2Param
+    EXPECT_EQ(_C2FieldId(8, 4), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mSigned32));
+    EXPECT_EQ(_C2FieldId(12, 8), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mSigned64));
+    EXPECT_EQ(_C2FieldId(28, 4), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mUnsigned32));
+    EXPECT_EQ(_C2FieldId(32, 8), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mUnsigned64));
+    EXPECT_EQ(_C2FieldId(40, 4), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mFloat));
+    EXPECT_EQ(_C2FieldId(44, 8), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mSize));
+    EXPECT_EQ(_C2FieldId(68, 1), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mBlob));
+    EXPECT_EQ(_C2FieldId(168, 1), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mString));
+    EXPECT_EQ(_C2FieldId(268, 1), _C2FieldId((C2TestAInfo*)0, &C2TestAInfo::mYesNo));
+
+    // NOTE: cannot use a member pointer for flex params due to introduction of 'm'
+    // EXPECT_EQ(_C2FieldId(8, 4), _C2FieldId(&C2TestFlexEndSizeInfo::m.mSigned32));
+    // EXPECT_EQ(_C2FieldId(12, 8), _C2FieldId(&C2TestFlexEndSizeInfo::m.mFlexSize));
+
+
+
+}
+
+struct S32 {
+    template<typename T, class B=typename std::remove_extent<T>::type>
+    inline S32(const T*) {
+        static_assert(!std::is_array<T>::value, "should not be an array");
+        static_assert(std::is_same<B, int32_t>::value, "should be int32_t");
+    }
+};
+
+struct FLX {
+    template<typename U, typename T, class B=typename std::remove_extent<T>::type>
+    inline FLX(const T*, const U*) {
+        static_assert(std::is_array<T>::value, "should be an array");
+        static_assert(std::extent<T>::value == 0, "should be an array of 0 extent");
+        static_assert(std::is_same<B, U>::value, "should be type U");
+    }
+};
+
+struct MP {
+    template<typename U, typename T, typename ExpectedU, typename UnexpectedU>
+    inline MP(T U::*, const ExpectedU*, const UnexpectedU*) {
+        static_assert(!std::is_same<U, UnexpectedU>::value, "should not be member pointer of the base type");
+        static_assert(std::is_same<U, ExpectedU>::value, "should be member pointer of the derived type");
+    }
+
+    template<typename U, typename T, typename B, typename D>
+    inline MP(T D::*, const D*) { }
+};
+
+void compiledStatic_arrayTypePropagationTest() {
+    (void)S32(&((C2TestFlexEndS32Struct *)0)->mSigned32);
+    (void)FLX(&((C2TestFlexEndS32Struct *)0)->mFlexSigned32, (int32_t*)0);
+    (void)FLX(&((C2TestFlexS32Struct *)0)->mFlexSigned32, (int32_t*)0);
+
+    typedef C2GlobalParam<C2Info, C2TestAStruct> C2TestAInfo;
+
+    // TRICKY: &derivedClass::baseMember has type of baseClass::*
+    static_assert(std::is_same<decltype(&C2TestAInfo::mSigned32), int32_t C2TestAStruct::*>::value,
+                  "base member pointer should have base class in type");
+
+    // therefore, member pointer expands to baseClass::* in templates
+    (void)MP(&C2TestAInfo::mSigned32,
+             (C2TestAStruct*)0 /* expected */, (C2TestAInfo*)0 /* unexpected */);
+    // but can be cast to derivedClass::*
+    (void)MP((int32_t C2TestAInfo::*)&C2TestAInfo::mSigned32,
+             (C2TestAInfo*)0 /* expected */, (C2TestAStruct*)0 /* unexpected */);
+
+    // TRICKY: baseClass::* does not autoconvert to derivedClass::* even in templates
+    // (void)MP(&C2TestAInfo::mSigned32, (C2TestAInfo*)0);
+}
+
+TEST_F(C2ParamTest, MemberPointerCast) {
+    typedef C2GlobalParam<C2Info, C2TestAStruct> C2TestAInfo;
+
+    static_assert(offsetof(C2TestAInfo, mSigned32) == 8, "offset should be 8");
+    constexpr int32_t C2TestAStruct::* s32ptr = &C2TestAInfo::mSigned32;
+    constexpr int32_t C2TestAInfo::* s32ptr_derived = (int32_t C2TestAStruct::*)&C2TestAInfo::mSigned32;
+    constexpr int32_t C2TestAInfo::* s32ptr_cast2derived = (int32_t C2TestAInfo::*)s32ptr;
+    C2TestAInfo *info = (C2TestAInfo *)256;
+    C2TestAStruct *strukt = (C2TestAStruct *)info;
+    int32_t *info_s32_derived = &(info->*s32ptr_derived);
+    int32_t *info_s32_cast2derived = &(info->*s32ptr_cast2derived);
+    int32_t *info_s32 = &(info->*s32ptr);
+    int32_t *strukt_s32 = &(strukt->*s32ptr);
+
+    EXPECT_EQ(256u, (uintptr_t)info);
+    EXPECT_EQ(264u, (uintptr_t)strukt);
+    EXPECT_EQ(264u, (uintptr_t)info_s32_derived);
+    EXPECT_EQ(264u, (uintptr_t)info_s32_cast2derived);
+    EXPECT_EQ(264u, (uintptr_t)info_s32);
+    EXPECT_EQ(264u, (uintptr_t)strukt_s32);
+
+    typedef C2GlobalParam<C2Info, C2TestFlexEndSizeStruct> C2TestFlexEndSizeInfo;
+    static_assert(offsetof(C2TestFlexEndSizeInfo, m.mSigned32) == 8, "offset should be 8");
+    static_assert(offsetof(C2TestFlexEndSizeInfo, m.mFlexSize) == 12, "offset should be 12");
+}
+
+/* ===================================== PARAM USAGE TESTS ===================================== */
+
+struct C2NumberStruct {
+    int32_t mNumber;
+    C2NumberStruct() {}
+    C2NumberStruct(int32_t _number) : mNumber(_number) {}
+
+    DEFINE_AND_DESCRIBE_C2STRUCT(Number)
+    C2FIELD(mNumber, "number")
+};
+
+struct C2NumbersStruct {
+    int32_t mNumbers[];
+    C2NumbersStruct() {}
+
+    DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(Numbers, mNumbers)
+    C2FIELD(mNumbers, "numbers")
+};
+static_assert(sizeof(C2NumbersStruct) == 0, "C2NumbersStruct has incorrect size");
+
+typedef C2GlobalParam<C2Tuning, C2NumberStruct> C2NumberTuning;
+typedef   C2PortParam<C2Tuning, C2NumberStruct> C2NumberPortTuning;
+typedef C2StreamParam<C2Tuning, C2NumberStruct> C2NumberStreamTuning;
+
+typedef C2GlobalParam<C2Tuning, C2NumbersStruct> C2NumbersTuning;
+typedef   C2PortParam<C2Tuning, C2NumbersStruct> C2NumbersPortTuning;
+typedef C2StreamParam<C2Tuning, C2NumbersStruct> C2NumbersStreamTuning;
+
+//
+#if 0
+
+void test() {
+    C2NumberStruct s(10);
+    (void)C2NumberStruct::fieldList;
+};
+
+typedef C2StreamParam<C2Tuning, C2Int64Value, kParamIndexNumberB> C2NumberConfig4;
+typedef C2PortParam<C2Tuning, C2Int32Value, kParamIndexNumber> C2NumberConfig3;
+typedef C2GlobalParam<C2Tuning, C2StringValue, kParamIndexNumber> C2VideoNameConfig;
+
+void test3() {
+    C2NumberConfig3 s(10);
+    s.mValue = 11;
+    s = 12;
+    (void)C2NumberConfig3::fieldList;
+    std::shared_ptr<C2VideoNameConfig> n = C2VideoNameConfig::alloc_shared(25);
+    strcpy(n->m.mValue, "lajos");
+    C2NumberConfig4 t(false, 0, 11);
+    t.mValue = 15;
+};
+
+struct C2NumbersStruct {
+    int32_t mNumbers[];
+    enum { baseIndex = kParamIndexNumber };
+    const static std::initializer_list<const C2FieldDescriptor> fieldList;
+    C2NumbersStruct() {}
+
+    FLEX(C2NumbersStruct, mNumbers);
+};
+
+static_assert(sizeof(C2NumbersStruct) == 0, "yes");
+
+
+typedef C2GlobalParam<C2Info, C2NumbersStruct> C2NumbersInfo;
+
+const std::initializer_list<const C2FieldDescriptor> C2NumbersStruct::fieldList =
+//    { { FD::INT32, 0, "widths" } };
+    { C2FieldDescriptor(&((C2NumbersStruct*)(nullptr))->mNumbers, "number") };
+
+typedef C2PortParam<C2Tuning, C2NumberStruct> C2NumberConfig;
+
+std::list<const C2FieldDescriptor> myList = C2NumberConfig::fieldList;
+
+    std::unique_ptr<android::C2ParamDescriptor> __test_describe(uint32_t paramType) {
+        std::list<const C2FieldDescriptor> fields = describeC2Params<C2NumberConfig>();
+
+        auto widths = C2NumbersInfo::alloc_shared(5);
+        widths->flexCount();
+        widths->m.mNumbers[4] = 1;
+
+        test();
+        test3();
+
+        C2NumberConfig outputWidth(false, 123);
+
+        C2Param::Index index(paramType);
+        switch (paramType) {
+        case C2NumberConfig::baseIndex:
+            return std::unique_ptr<C2ParamDescriptor>(new C2ParamDescriptor{
+                true /* isRequired */,
+                "number",
+                index,
+            });
+        }
+        return nullptr;
+    }
+
+
+} // namespace android
+
+#endif
+//
+
+template<typename T>
+bool canSetPort(T &o, bool output) { return o.setPort(output); }
+bool canSetPort(...) { return false; }
+
+template<typename S, typename=decltype(((S*)0)->setPort(true))>
+static std::true_type _canCallSetPort(int);
+template<typename>
+static std::false_type _canCallSetPort(...);
+#define canCallSetPort(x) decltype(_canCallSetPort<std::remove_reference<decltype(x)>::type>(0))::value
+
+/* ======================================= STATIC TESTS ======================================= */
+
+static_assert(_C2Comparable<int>::value, "int is not comparable");
+static_assert(!_C2Comparable<void>::value, "void is comparable");
+
+struct C2_HIDE _test0 {
+    bool operator==(const _test0&);
+    bool operator!=(const _test0&);
+};
+struct C2_HIDE _test1 {
+    bool operator==(const _test1&);
+};
+struct C2_HIDE _test2 {
+    bool operator!=(const _test2&);
+};
+static_assert(_C2Comparable<_test0>::value, "class with == and != is not comparable");
+static_assert(_C2Comparable<_test1>::value, "class with == is not comparable");
+static_assert(_C2Comparable<_test2>::value, "class with != is not comparable");
+
+/* ======================================= C2PARAM TESTS ======================================= */
+
+struct _C2ParamInspector {
+    static void StaticTest();
+    static void StaticFlexTest();
+};
+
+// TEST_F(_C2ParamInspector, StaticTest) {
+void _C2ParamInspector::StaticTest() {
+    typedef C2Param::Index I;
+
+    // C2NumberStruct: baseIndex = kIndex                          (args)
+    static_assert(C2NumberStruct::baseIndex == kParamIndexNumber, "bad index");
+    static_assert(sizeof(C2NumberStruct) == 4, "bad size");
+
+    // C2NumberTuning:             kIndex | tun | global           (args)
+    static_assert(C2NumberTuning::baseIndex == kParamIndexNumber, "bad index");
+    static_assert(C2NumberTuning::typeIndex == (kParamIndexNumber | I::kTypeTuning | I::kDirGlobal), "bad index");
+    static_assert(sizeof(C2NumberTuning) == 12, "bad size");
+
+    static_assert(offsetof(C2NumberTuning, _mSize) == 0, "bad size");
+    static_assert(offsetof(C2NumberTuning, _mIndex) == 4, "bad offset");
+    static_assert(offsetof(C2NumberTuning, mNumber) == 8, "bad offset");
+
+    // C2NumberPortTuning:         kIndex | tun | port             (bool, args)
+    static_assert(sizeof(C2NumberPortTuning) == 12, "bad size");
+    // C2NumberPortTuning::input:  kIndex | tun | port | input     (args)
+    // C2NumberPortTuning::output: kIndex | tun | port | output    (args)
+    static_assert(C2NumberPortTuning::input::baseIndex ==
+                  kParamIndexNumber, "bad index");
+    static_assert(C2NumberPortTuning::input::typeIndex ==
+                  (kParamIndexNumber | I::kTypeTuning | I::kDirInput), "bad index");
+    static_assert(C2NumberPortTuning::output::baseIndex ==
+                  kParamIndexNumber, "bad index");
+    static_assert(C2NumberPortTuning::output::typeIndex ==
+                  (kParamIndexNumber | I::kTypeTuning | I::kDirOutput), "bad index");
+    static_assert(sizeof(C2NumberPortTuning::input) == 12, "bad size");
+    static_assert(sizeof(C2NumberPortTuning::output) == 12, "bad size");
+    static_assert(offsetof(C2NumberPortTuning::input, _mSize) == 0, "bad size");
+    static_assert(offsetof(C2NumberPortTuning::input, _mIndex) == 4, "bad offset");
+    static_assert(offsetof(C2NumberPortTuning::input, mNumber) == 8, "bad offset");
+    static_assert(offsetof(C2NumberPortTuning::output, _mSize) == 0, "bad size");
+    static_assert(offsetof(C2NumberPortTuning::output, _mIndex) == 4, "bad offset");
+    static_assert(offsetof(C2NumberPortTuning::output, mNumber) == 8, "bad offset");
+
+    // C2NumberStreamTuning:       kIndex | tun | str              (bool, uint, args)
+    static_assert(sizeof(C2NumberStreamTuning) == 12u, "bad size");
+    // C2NumberStreamTuning::input kIndex | tun | str | input      (int, args)
+    // C2NumberStreamTuning::output kIx   | tun | str | output     (int, args)
+    static_assert(C2NumberStreamTuning::input::baseIndex ==
+                  kParamIndexNumber, "bad index");
+    static_assert(C2NumberStreamTuning::input::typeIndex ==
+                  (kParamIndexNumber | I::kTypeTuning | I::kDirInput | I::kStreamFlag), "bad index");
+    static_assert(C2NumberStreamTuning::output::baseIndex ==
+                  kParamIndexNumber, "bad index");
+    static_assert(C2NumberStreamTuning::output::typeIndex ==
+                  (kParamIndexNumber | I::kTypeTuning | I::kDirOutput | I::kStreamFlag), "bad index");
+    static_assert(sizeof(C2NumberStreamTuning::input) == 12u, "bad size");
+    static_assert(sizeof(C2NumberStreamTuning::output) == 12u, "bad size");
+    static_assert(offsetof(C2NumberStreamTuning::input, _mSize) == 0, "bad size");
+    static_assert(offsetof(C2NumberStreamTuning::input, _mIndex) == 4, "bad offset");
+    static_assert(offsetof(C2NumberStreamTuning::input, mNumber) == 8, "bad offset");
+    static_assert(offsetof(C2NumberStreamTuning::output, _mSize) == 0, "bad size");
+    static_assert(offsetof(C2NumberStreamTuning::output, _mIndex) == 4, "bad offset");
+    static_assert(offsetof(C2NumberStreamTuning::output, mNumber) == 8, "bad offset");
+}
+
+void _C2ParamInspector::StaticFlexTest() {
+    typedef C2Param::Index I;
+
+    // C2NumbersStruct: baseIndex = kIndex                          (args)
+    static_assert(C2NumbersStruct::baseIndex == (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+    static_assert(sizeof(C2NumbersStruct) == 0, "bad size");
+
+    // C2NumbersTuning:             kIndex | tun | global           (args)
+    static_assert(C2NumbersTuning::baseIndex == (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+    static_assert(C2NumbersTuning::typeIndex == (I::kFlexibleFlag | kParamIndexNumbers | I::kTypeTuning | I::kDirGlobal), "bad index");
+    static_assert(sizeof(C2NumbersTuning) == 8, "bad size");
+
+    static_assert(offsetof(C2NumbersTuning, _mSize) == 0, "bad size");
+    static_assert(offsetof(C2NumbersTuning, _mIndex) == 4, "bad offset");
+    static_assert(offsetof(C2NumbersTuning, m.mNumbers) == 8, "bad offset");
+
+    // C2NumbersPortTuning:         kIndex | tun | port             (bool, args)
+    static_assert(sizeof(C2NumbersPortTuning) == 8, "bad size");
+    // C2NumbersPortTuning::input:  kIndex | tun | port | input     (args)
+    // C2NumbersPortTuning::output: kIndex | tun | port | output    (args)
+    static_assert(C2NumbersPortTuning::input::baseIndex ==
+                  (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+    static_assert(C2NumbersPortTuning::input::typeIndex ==
+                  (I::kFlexibleFlag | kParamIndexNumbers | I::kTypeTuning | I::kDirInput), "bad index");
+    static_assert(C2NumbersPortTuning::output::baseIndex ==
+                  (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+    static_assert(C2NumbersPortTuning::output::typeIndex ==
+                  (I::kFlexibleFlag | kParamIndexNumbers | I::kTypeTuning | I::kDirOutput), "bad index");
+    static_assert(sizeof(C2NumbersPortTuning::input) == 8, "bad size");
+    static_assert(sizeof(C2NumbersPortTuning::output) == 8, "bad size");
+    static_assert(offsetof(C2NumbersPortTuning::input, _mSize) == 0, "bad size");
+    static_assert(offsetof(C2NumbersPortTuning::input, _mIndex) == 4, "bad offset");
+    static_assert(offsetof(C2NumbersPortTuning::input, m.mNumbers) == 8, "bad offset");
+    static_assert(offsetof(C2NumbersPortTuning::output, _mSize) == 0, "bad size");
+    static_assert(offsetof(C2NumbersPortTuning::output, _mIndex) == 4, "bad offset");
+    static_assert(offsetof(C2NumbersPortTuning::output, m.mNumbers) == 8, "bad offset");
+
+    // C2NumbersStreamTuning:       kIndex | tun | str              (bool, uint, args)
+    static_assert(sizeof(C2NumbersStreamTuning) == 8, "bad size");
+    // C2NumbersStreamTuning::input kIndex | tun | str | input      (int, args)
+    // C2NumbersStreamTuning::output kIx   | tun | str | output     (int, args)
+    static_assert(C2NumbersStreamTuning::input::baseIndex ==
+                  (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+    static_assert(C2NumbersStreamTuning::input::typeIndex ==
+                  (I::kFlexibleFlag | kParamIndexNumbers | I::kTypeTuning | I::kDirInput | I::kStreamFlag), "bad index");
+    static_assert(C2NumbersStreamTuning::output::baseIndex ==
+                  (I::kFlexibleFlag | kParamIndexNumbers), "bad index");
+    static_assert(C2NumbersStreamTuning::output::typeIndex ==
+                  (I::kFlexibleFlag | kParamIndexNumbers | I::kTypeTuning | I::kDirOutput | I::kStreamFlag), "bad index");
+    static_assert(sizeof(C2NumbersStreamTuning::input) == 8, "bad size");
+    static_assert(sizeof(C2NumbersStreamTuning::output) == 8, "bad size");
+    static_assert(offsetof(C2NumbersStreamTuning::input, _mSize) == 0, "bad size");
+    static_assert(offsetof(C2NumbersStreamTuning::input, _mIndex) == 4, "bad offset");
+    static_assert(offsetof(C2NumbersStreamTuning::input, m.mNumbers) == 8, "bad offset");
+    static_assert(offsetof(C2NumbersStreamTuning::output, _mSize) == 0, "bad size");
+    static_assert(offsetof(C2NumbersStreamTuning::output, _mIndex) == 4, "bad offset");
+    static_assert(offsetof(C2NumbersStreamTuning::output, m.mNumbers) == 8, "bad offset");
+}
+
+TEST_F(C2ParamTest, ParamOpsTest) {
+    const C2NumberStruct str(100);
+    C2NumberStruct bstr;
+
+    {
+        EXPECT_EQ(100, str.mNumber);
+        bstr.mNumber = 100;
+
+        C2Param::BaseIndex index = C2NumberStruct::baseIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_FALSE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+    }
+
+    const C2NumberTuning tun(100);
+    C2NumberTuning btun;
+
+    {
+        // flags & invariables
+        for (const auto &p : { tun, btun }) {
+            EXPECT_TRUE((bool)p);
+            EXPECT_FALSE(!p);
+            EXPECT_EQ(12u, p.size());
+
+            EXPECT_FALSE(p.isVendor());
+            EXPECT_FALSE(p.isFlexible());
+            EXPECT_TRUE(p.isGlobal());
+            EXPECT_FALSE(p.forInput());
+            EXPECT_FALSE(p.forOutput());
+            EXPECT_FALSE(p.forStream());
+            EXPECT_FALSE(p.forPort());
+        }
+
+        // value
+        EXPECT_EQ(100, tun.mNumber);
+        EXPECT_EQ(0, btun.mNumber);
+        EXPECT_FALSE(tun == btun);
+        EXPECT_FALSE(tun.operator==(btun));
+        EXPECT_TRUE(tun != btun);
+        EXPECT_TRUE(tun.operator!=(btun));
+        btun.mNumber = 100;
+        EXPECT_EQ(tun, btun);
+
+        // index
+        EXPECT_EQ(C2Param::Type(tun.type()).baseIndex(), C2NumberStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(tun.type()).paramIndex(), kParamIndexNumber);
+        EXPECT_EQ(tun.type(), C2NumberTuning::typeIndex);
+        EXPECT_EQ(tun.stream(), ~0u);
+
+        C2Param::BaseIndex index = C2NumberTuning::baseIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_FALSE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+
+        C2Param::Type type = C2NumberTuning::typeIndex;
+        EXPECT_FALSE(type.isVendor());
+        EXPECT_FALSE(type.isFlexible());
+        EXPECT_TRUE(type.isGlobal());
+        EXPECT_FALSE(type.forInput());
+        EXPECT_FALSE(type.forOutput());
+        EXPECT_FALSE(type.forStream());
+        EXPECT_FALSE(type.forPort());
+
+        EXPECT_EQ(C2NumberTuning::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumberTuning::From(&tun), &tun);
+        EXPECT_EQ(C2NumberPortTuning::From(&tun), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::input::From(&tun), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::output::From(&tun), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::From(&tun), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::input::From(&tun), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::output::From(&tun), nullptr);
+    }
+
+    const C2NumberPortTuning outp1(true, 100), inp1(false, 100);
+    C2NumberPortTuning boutp1, binp1, binp3(false, 100);
+    const C2NumberPortTuning::input inp2(100);
+    C2NumberPortTuning::input binp2;
+    const C2NumberPortTuning::output outp2(100);
+    C2NumberPortTuning::output boutp2;
+
+    {
+        static_assert(canCallSetPort(binp3), "should be able to");
+        static_assert(canCallSetPort(binp1), "should be able to");
+        static_assert(!canCallSetPort(inp1), "should not be able to (const)");
+        static_assert(!canCallSetPort(inp2), "should not be able to (const & type)");
+        static_assert(!canCallSetPort(binp2), "should not be able to (type)");
+
+        // flags & invariables
+        for (const auto &p : { outp1, inp1, boutp1 }) {
+            EXPECT_EQ(12u, p.size());
+            EXPECT_FALSE(p.isVendor());
+            EXPECT_FALSE(p.isFlexible());
+            EXPECT_FALSE(p.isGlobal());
+            EXPECT_FALSE(p.forStream());
+            EXPECT_TRUE(p.forPort());
+        }
+        for (const auto &p : { inp2, binp2 }) {
+            EXPECT_EQ(12u, p.size());
+            EXPECT_FALSE(p.isVendor());
+            EXPECT_FALSE(p.isFlexible());
+            EXPECT_FALSE(p.isGlobal());
+            EXPECT_FALSE(p.forStream());
+            EXPECT_TRUE(p.forPort());
+        }
+        for (const auto &p : { outp2, boutp2 }) {
+            EXPECT_EQ(12u, p.size());
+            EXPECT_FALSE(p.isVendor());
+            EXPECT_FALSE(p.isFlexible());
+            EXPECT_FALSE(p.isGlobal());
+            EXPECT_FALSE(p.forStream());
+            EXPECT_TRUE(p.forPort());
+        }
+
+        // port specific flags & invariables
+        EXPECT_FALSE(outp1.forInput());
+        EXPECT_TRUE(outp1.forOutput());
+
+        EXPECT_TRUE(inp1.forInput());
+        EXPECT_FALSE(inp1.forOutput());
+
+        for (const auto &p : { outp1, inp1 }) {
+            EXPECT_TRUE((bool)p);
+            EXPECT_FALSE(!p);
+            EXPECT_EQ(100, p.mNumber);
+        }
+        for (const auto &p : { outp2, boutp2 }) {
+            EXPECT_TRUE((bool)p);
+            EXPECT_FALSE(!p);
+
+            EXPECT_FALSE(p.forInput());
+            EXPECT_TRUE(p.forOutput());
+        }
+        for (const auto &p : { inp2, binp2 }) {
+            EXPECT_TRUE((bool)p);
+            EXPECT_FALSE(!p);
+
+            EXPECT_TRUE(p.forInput());
+            EXPECT_FALSE(p.forOutput());
+        }
+        for (const auto &p : { boutp1 } ) {
+            EXPECT_FALSE((bool)p);
+            EXPECT_TRUE(!p);
+
+            EXPECT_FALSE(p.forInput());
+            EXPECT_FALSE(p.forOutput());
+            EXPECT_EQ(0, p.mNumber);
+        }
+
+        // values
+        EXPECT_EQ(100, inp2.mNumber);
+        EXPECT_EQ(100, outp2.mNumber);
+        EXPECT_EQ(0, binp1.mNumber);
+        EXPECT_EQ(0, binp2.mNumber);
+        EXPECT_EQ(0, boutp1.mNumber);
+        EXPECT_EQ(0, boutp2.mNumber);
+
+        EXPECT_TRUE(inp1 != outp1);
+        EXPECT_TRUE(inp1 == inp2);
+        EXPECT_TRUE(outp1 == outp2);
+        EXPECT_TRUE(binp1 == boutp1);
+        EXPECT_TRUE(binp2 != boutp2);
+
+        EXPECT_TRUE(inp1 != binp1);
+        binp1.mNumber = 100;
+        EXPECT_TRUE(inp1 != binp1);
+        binp1.setPort(false /* output */);
+        EXPECT_TRUE((bool)binp1);
+        EXPECT_FALSE(!binp1);
+        EXPECT_TRUE(inp1 == binp1);
+
+        EXPECT_TRUE(inp2 != binp2);
+        binp2.mNumber = 100;
+        EXPECT_TRUE(inp2 == binp2);
+
+        binp1.setPort(true /* output */);
+        EXPECT_TRUE(outp1 == binp1);
+
+        EXPECT_TRUE(outp1 != boutp1);
+        boutp1.mNumber = 100;
+        EXPECT_TRUE(outp1 != boutp1);
+        boutp1.setPort(true /* output */);
+        EXPECT_TRUE((bool)boutp1);
+        EXPECT_FALSE(!boutp1);
+        EXPECT_TRUE(outp1 == boutp1);
+
+        EXPECT_TRUE(outp2 != boutp2);
+        boutp2.mNumber = 100;
+        EXPECT_TRUE(outp2 == boutp2);
+
+        boutp1.setPort(false /* output */);
+        EXPECT_TRUE(inp1 == boutp1);
+
+        // index
+        EXPECT_EQ(C2Param::Type(inp1.type()).baseIndex(), C2NumberStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(inp1.type()).paramIndex(), kParamIndexNumber);
+        EXPECT_EQ(inp1.type(), C2NumberPortTuning::input::typeIndex);
+        EXPECT_EQ(inp1.stream(), ~0u);
+
+        EXPECT_EQ(C2Param::Type(inp2.type()).baseIndex(), C2NumberStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(inp2.type()).paramIndex(), kParamIndexNumber);
+        EXPECT_EQ(inp2.type(), C2NumberPortTuning::input::typeIndex);
+        EXPECT_EQ(inp2.stream(), ~0u);
+
+        EXPECT_EQ(C2Param::Type(outp1.type()).baseIndex(), C2NumberStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(outp1.type()).paramIndex(), kParamIndexNumber);
+        EXPECT_EQ(outp1.type(), C2NumberPortTuning::output::typeIndex);
+        EXPECT_EQ(outp1.stream(), ~0u);
+
+        EXPECT_EQ(C2Param::Type(outp2.type()).baseIndex(), C2NumberStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(outp2.type()).paramIndex(), kParamIndexNumber);
+        EXPECT_EQ(outp2.type(), C2NumberPortTuning::output::typeIndex);
+        EXPECT_EQ(outp2.stream(), ~0u);
+
+        C2Param::BaseIndex index = C2NumberPortTuning::input::typeIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_FALSE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+
+        index = C2NumberPortTuning::output::typeIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_FALSE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+
+        C2Param::Type type = C2NumberPortTuning::input::typeIndex;
+        EXPECT_FALSE(type.isVendor());
+        EXPECT_FALSE(type.isFlexible());
+        EXPECT_FALSE(type.isGlobal());
+        EXPECT_TRUE(type.forInput());
+        EXPECT_FALSE(type.forOutput());
+        EXPECT_FALSE(type.forStream());
+        EXPECT_TRUE(type.forPort());
+
+        type = C2NumberPortTuning::output::typeIndex;
+        EXPECT_FALSE(type.isVendor());
+        EXPECT_FALSE(type.isFlexible());
+        EXPECT_FALSE(type.isGlobal());
+        EXPECT_FALSE(type.forInput());
+        EXPECT_TRUE(type.forOutput());
+        EXPECT_FALSE(type.forStream());
+        EXPECT_TRUE(type.forPort());
+
+        EXPECT_EQ(C2NumberPortTuning::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::input::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::output::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumberTuning::From(&inp1), nullptr);
+        EXPECT_EQ(C2NumberTuning::From(&inp2), nullptr);
+        EXPECT_EQ(C2NumberTuning::From(&outp1), nullptr);
+        EXPECT_EQ(C2NumberTuning::From(&outp2), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::From(&inp1), &inp1);
+        EXPECT_EQ(C2NumberPortTuning::From(&inp2), (C2NumberPortTuning*)&inp2);
+        EXPECT_EQ(C2NumberPortTuning::From(&outp1), &outp1);
+        EXPECT_EQ(C2NumberPortTuning::From(&outp2), (C2NumberPortTuning*)&outp2);
+        EXPECT_EQ(C2NumberPortTuning::input::From(&inp1), (C2NumberPortTuning::input*)&inp1);
+        EXPECT_EQ(C2NumberPortTuning::input::From(&inp2), &inp2);
+        EXPECT_EQ(C2NumberPortTuning::input::From(&outp1), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::input::From(&outp2), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::output::From(&inp1), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::output::From(&inp2), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::output::From(&outp1), (C2NumberPortTuning::output*)&outp1);
+        EXPECT_EQ(C2NumberPortTuning::output::From(&outp2), &outp2);
+        EXPECT_EQ(C2NumberStreamTuning::From(&inp1), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::From(&inp2), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::From(&outp1), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::From(&outp2), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::input::From(&inp1), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::input::From(&inp2), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::input::From(&outp1), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::input::From(&outp2), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::output::From(&inp1), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::output::From(&inp2), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::output::From(&outp1), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::output::From(&outp2), nullptr);
+    }
+
+    const C2NumberStreamTuning outs1(true, 1u, 100), ins1(false, 1u, 100);
+    C2NumberStreamTuning bouts1, bins1, bins3(false, 1u, 100);
+    const C2NumberStreamTuning::input ins2(1u, 100);
+    C2NumberStreamTuning::input bins2;
+    const C2NumberStreamTuning::output outs2(1u, 100);
+    C2NumberStreamTuning::output bouts2;
+
+    {
+        static_assert(canCallSetPort(bins3), "should be able to");
+        static_assert(canCallSetPort(bins1), "should be able to");
+        static_assert(!canCallSetPort(ins1), "should not be able to (const)");
+        static_assert(!canCallSetPort(ins2), "should not be able to (const & type)");
+        static_assert(!canCallSetPort(bins2), "should not be able to (type)");
+
+        // flags & invariables
+        for (const auto &p : { outs1, ins1, bouts1 }) {
+            EXPECT_EQ(12u, p.size());
+            EXPECT_FALSE(p.isVendor());
+            EXPECT_FALSE(p.isFlexible());
+            EXPECT_FALSE(p.isGlobal());
+            EXPECT_TRUE(p.forStream());
+            EXPECT_FALSE(p.forPort());
+        }
+        for (const auto &p : { ins2, bins2 }) {
+            EXPECT_EQ(12u, p.size());
+            EXPECT_FALSE(p.isVendor());
+            EXPECT_FALSE(p.isFlexible());
+            EXPECT_FALSE(p.isGlobal());
+            EXPECT_TRUE(p.forStream());
+            EXPECT_FALSE(p.forPort());
+        }
+        for (const auto &p : { outs2, bouts2 }) {
+            EXPECT_EQ(12u, p.size());
+            EXPECT_FALSE(p.isVendor());
+            EXPECT_FALSE(p.isFlexible());
+            EXPECT_FALSE(p.isGlobal());
+            EXPECT_TRUE(p.forStream());
+            EXPECT_FALSE(p.forPort());
+        }
+
+        // port specific flags & invariables
+        EXPECT_FALSE(outs1.forInput());
+        EXPECT_TRUE(outs1.forOutput());
+
+        EXPECT_TRUE(ins1.forInput());
+        EXPECT_FALSE(ins1.forOutput());
+
+        for (const auto &p : { outs1, ins1 }) {
+            EXPECT_TRUE((bool)p);
+            EXPECT_FALSE(!p);
+            EXPECT_EQ(100, p.mNumber);
+            EXPECT_EQ(1u, p.stream());
+        }
+        for (const auto &p : { outs2, bouts2 }) {
+            EXPECT_TRUE((bool)p);
+            EXPECT_FALSE(!p);
+
+            EXPECT_FALSE(p.forInput());
+            EXPECT_TRUE(p.forOutput());
+        }
+        for (const auto &p : { ins2, bins2 }) {
+            EXPECT_TRUE((bool)p);
+            EXPECT_FALSE(!p);
+
+            EXPECT_TRUE(p.forInput());
+            EXPECT_FALSE(p.forOutput());
+        }
+        for (const auto &p : { bouts1 } ) {
+            EXPECT_FALSE((bool)p);
+            EXPECT_TRUE(!p);
+
+            EXPECT_FALSE(p.forInput());
+            EXPECT_FALSE(p.forOutput());
+            EXPECT_EQ(0, p.mNumber);
+        }
+
+        // values
+        EXPECT_EQ(100, ins2.mNumber);
+        EXPECT_EQ(100, outs2.mNumber);
+        EXPECT_EQ(0, bins1.mNumber);
+        EXPECT_EQ(0, bins2.mNumber);
+        EXPECT_EQ(0, bouts1.mNumber);
+        EXPECT_EQ(0, bouts2.mNumber);
+
+        EXPECT_EQ(1u, ins2.stream());
+        EXPECT_EQ(1u, outs2.stream());
+        EXPECT_EQ(0u, bins1.stream());
+        EXPECT_EQ(0u, bins2.stream());
+        EXPECT_EQ(0u, bouts1.stream());
+        EXPECT_EQ(0u, bouts2.stream());
+
+        EXPECT_TRUE(ins1 != outs1);
+        EXPECT_TRUE(ins1 == ins2);
+        EXPECT_TRUE(outs1 == outs2);
+        EXPECT_TRUE(bins1 == bouts1);
+        EXPECT_TRUE(bins2 != bouts2);
+
+        EXPECT_TRUE(ins1 != bins1);
+        bins1.mNumber = 100;
+        EXPECT_TRUE(ins1 != bins1);
+        bins1.setPort(false /* output */);
+        EXPECT_TRUE(ins1 != bins1);
+        bins1.setStream(1u);
+        EXPECT_TRUE(ins1 == bins1);
+
+        EXPECT_TRUE(ins2 != bins2);
+        bins2.mNumber = 100;
+        EXPECT_TRUE(ins2 != bins2);
+        bins2.setStream(1u);
+        EXPECT_TRUE(ins2 == bins2);
+
+        bins1.setPort(true /* output */);
+        EXPECT_TRUE(outs1 == bins1);
+
+        EXPECT_TRUE(outs1 != bouts1);
+        bouts1.mNumber = 100;
+        EXPECT_TRUE(outs1 != bouts1);
+        bouts1.setPort(true /* output */);
+        EXPECT_TRUE(outs1 != bouts1);
+        bouts1.setStream(1u);
+        EXPECT_TRUE(outs1 == bouts1);
+
+        EXPECT_TRUE(outs2 != bouts2);
+        bouts2.mNumber = 100;
+        EXPECT_TRUE(outs2 != bouts2);
+        bouts2.setStream(1u);
+        EXPECT_TRUE(outs2 == bouts2);
+
+        bouts1.setPort(false /* output */);
+        EXPECT_TRUE(ins1 == bouts1);
+
+        // index
+        EXPECT_EQ(C2Param::Type(ins1.type()).baseIndex(), C2NumberStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(ins1.type()).paramIndex(), kParamIndexNumber);
+        EXPECT_EQ(ins1.type(), C2NumberStreamTuning::input::typeIndex);
+
+        EXPECT_EQ(C2Param::Type(ins2.type()).baseIndex(), C2NumberStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(ins2.type()).paramIndex(), kParamIndexNumber);
+        EXPECT_EQ(ins2.type(), C2NumberStreamTuning::input::typeIndex);
+
+        EXPECT_EQ(C2Param::Type(outs1.type()).baseIndex(), C2NumberStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(outs1.type()).paramIndex(), kParamIndexNumber);
+        EXPECT_EQ(outs1.type(), C2NumberStreamTuning::output::typeIndex);
+
+        EXPECT_EQ(C2Param::Type(outs2.type()).baseIndex(), C2NumberStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(outs2.type()).paramIndex(), kParamIndexNumber);
+        EXPECT_EQ(outs2.type(), C2NumberStreamTuning::output::typeIndex);
+
+        C2Param::BaseIndex index = C2NumberStreamTuning::input::typeIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_FALSE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+
+        index = C2NumberStreamTuning::output::typeIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_FALSE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumber);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumber);
+
+        C2Param::Type type = C2NumberStreamTuning::input::typeIndex;
+        EXPECT_FALSE(type.isVendor());
+        EXPECT_FALSE(type.isFlexible());
+        EXPECT_FALSE(type.isGlobal());
+        EXPECT_TRUE(type.forInput());
+        EXPECT_FALSE(type.forOutput());
+        EXPECT_TRUE(type.forStream());
+        EXPECT_FALSE(type.forPort());
+
+        type = C2NumberStreamTuning::output::typeIndex;
+        EXPECT_FALSE(type.isVendor());
+        EXPECT_FALSE(type.isFlexible());
+        EXPECT_FALSE(type.isGlobal());
+        EXPECT_FALSE(type.forInput());
+        EXPECT_TRUE(type.forOutput());
+        EXPECT_TRUE(type.forStream());
+        EXPECT_FALSE(type.forPort());
+
+        EXPECT_EQ(C2NumberPortTuning::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::input::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::output::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumberTuning::From(&ins1), nullptr);
+        EXPECT_EQ(C2NumberTuning::From(&ins2), nullptr);
+        EXPECT_EQ(C2NumberTuning::From(&outs1), nullptr);
+        EXPECT_EQ(C2NumberTuning::From(&outs2), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::From(&ins1), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::From(&ins2), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::From(&outs1), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::From(&outs2), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::input::From(&ins1), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::input::From(&ins2), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::input::From(&outs1), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::input::From(&outs2), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::output::From(&ins1), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::output::From(&ins2), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::output::From(&outs1), nullptr);
+        EXPECT_EQ(C2NumberPortTuning::output::From(&outs2), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::From(&ins1), &ins1);
+        EXPECT_EQ(C2NumberStreamTuning::From(&ins2), (C2NumberStreamTuning*)&ins2);
+        EXPECT_EQ(C2NumberStreamTuning::From(&outs1), &outs1);
+        EXPECT_EQ(C2NumberStreamTuning::From(&outs2), (C2NumberStreamTuning*)&outs2);
+        EXPECT_EQ(C2NumberStreamTuning::input::From(&ins1), (C2NumberStreamTuning::input*)&ins1);
+        EXPECT_EQ(C2NumberStreamTuning::input::From(&ins2), &ins2);
+        EXPECT_EQ(C2NumberStreamTuning::input::From(&outs1), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::input::From(&outs2), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::output::From(&ins1), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::output::From(&ins2), nullptr);
+        EXPECT_EQ(C2NumberStreamTuning::output::From(&outs1), (C2NumberStreamTuning::output*)&outs1);
+        EXPECT_EQ(C2NumberStreamTuning::output::From(&outs2), &outs2);
+
+    }
+
+    {
+        uint32_t videoWidth[] = { 12u, C2NumberStreamTuning::output::typeIndex, 100 };
+        C2Param *p1 = C2Param::From(videoWidth, sizeof(videoWidth));
+        EXPECT_NE(p1, nullptr);
+        EXPECT_EQ(12u, p1->size());
+        EXPECT_EQ(p1->type(), C2NumberStreamTuning::output::typeIndex);
+
+        p1 = C2Param::From(videoWidth, sizeof(videoWidth) + 2);
+        EXPECT_EQ(p1, nullptr);
+
+        p1 = C2Param::From(videoWidth, sizeof(videoWidth) - 2);
+        EXPECT_EQ(p1, nullptr);
+
+        p1 = C2Param::From(videoWidth, 3);
+        EXPECT_EQ(p1, nullptr);
+
+        p1 = C2Param::From(videoWidth, 0);
+        EXPECT_EQ(p1, nullptr);
+    }
+}
+
+void StaticTestAddBaseIndex() {
+    struct nobase {};
+    struct base { enum : uint32_t { baseIndex = 1 }; };
+    static_assert(C2AddBaseIndex<nobase, 2>::baseIndex == 2, "should be 2");
+    static_assert(C2AddBaseIndex<base, 1>::baseIndex == 1, "should be 1");
+}
+
+class TestFlexHelper {
+    struct _Flex {
+        int32_t a;
+        char b[];
+        _Flex() {}
+        FLEX(_Flex, b);
+    };
+
+    struct _BoFlex {
+        _Flex a;
+        _BoFlex() {}
+        FLEX(_BoFlex, a);
+    };
+
+    struct _NonFlex {
+    };
+
+
+    static void StaticTest() {
+        static_assert(std::is_same<_C2FlexHelper<char>::flexType, void>::value, "should be void");
+        static_assert(std::is_same<_C2FlexHelper<char[]>::flexType, char>::value, "should be char");
+        static_assert(std::is_same<_C2FlexHelper<_Flex>::flexType, char>::value, "should be char");
+
+        static_assert(std::is_same<_C2FlexHelper<_BoFlex>::flexType, char>::value, "should be void");
+
+        static_assert(_C2Flexible<_Flex>::value, "should be flexible");
+        static_assert(!_C2Flexible<_NonFlex>::value, "should not be flexible");
+    }
+};
+
+TEST_F(C2ParamTest, FlexParamOpsTest) {
+//    const C2NumbersStruct str{100};
+    C2NumbersStruct bstr;
+    {
+//        EXPECT_EQ(100, str->m.mNumbers[0]);
+        (void)&bstr.mNumbers[0];
+
+        C2Param::BaseIndex index = C2NumbersStruct::baseIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_TRUE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+    }
+
+    std::unique_ptr<C2NumbersTuning> tun_ = C2NumbersTuning::alloc_unique(1);
+    tun_->m.mNumbers[0] = 100;
+    std::unique_ptr<const C2NumbersTuning> tun = std::move(tun_);
+    std::shared_ptr<C2NumbersTuning> btun = C2NumbersTuning::alloc_shared(1);
+
+    {
+        // flags & invariables
+        const C2NumbersTuning *T[] = { tun.get(), btun.get() };
+        for (const auto p : T) {
+            EXPECT_TRUE((bool)(*p));
+            EXPECT_FALSE(!(*p));
+            EXPECT_EQ(12u, p->size());
+
+            EXPECT_FALSE(p->isVendor());
+            EXPECT_TRUE(p->isFlexible());
+            EXPECT_TRUE(p->isGlobal());
+            EXPECT_FALSE(p->forInput());
+            EXPECT_FALSE(p->forOutput());
+            EXPECT_FALSE(p->forStream());
+            EXPECT_FALSE(p->forPort());
+        }
+
+        // value
+        EXPECT_EQ(100, tun->m.mNumbers[0]);
+        EXPECT_EQ(0, btun->m.mNumbers[0]);
+        EXPECT_FALSE(*tun == *btun);
+        EXPECT_FALSE(tun->operator==(*btun));
+        EXPECT_TRUE(*tun != *btun);
+        EXPECT_TRUE(tun->operator!=(*btun));
+        btun->m.mNumbers[0] = 100;
+        EXPECT_EQ(*tun, *btun);
+
+        // index
+        EXPECT_EQ(C2Param::Type(tun->type()).baseIndex(), C2NumbersStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(tun->type()).paramIndex(), kParamIndexNumbers);
+        EXPECT_EQ(tun->type(), C2NumbersTuning::typeIndex);
+        EXPECT_EQ(tun->stream(), ~0u);
+
+        C2Param::BaseIndex index = C2NumbersTuning::baseIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_TRUE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+
+        C2Param::Type type = C2NumbersTuning::typeIndex;
+        EXPECT_FALSE(type.isVendor());
+        EXPECT_TRUE(type.isFlexible());
+        EXPECT_TRUE(type.isGlobal());
+        EXPECT_FALSE(type.forInput());
+        EXPECT_FALSE(type.forOutput());
+        EXPECT_FALSE(type.forStream());
+        EXPECT_FALSE(type.forPort());
+
+        EXPECT_EQ(C2NumbersTuning::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumbersTuning::From(tun.get()), tun.get());
+        EXPECT_EQ(C2NumbersPortTuning::From(tun.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::input::From(tun.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::output::From(tun.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::From(tun.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::input::From(tun.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::output::From(tun.get()), nullptr);
+    }
+
+    std::unique_ptr<C2NumbersPortTuning> outp1_(C2NumbersPortTuning::alloc_unique(1, true)),
+            inp1_ = C2NumbersPortTuning::alloc_unique(1, false);
+    outp1_->m.mNumbers[0] = 100;
+    inp1_->m.mNumbers[0] = 100;
+    std::unique_ptr<const C2NumbersPortTuning> outp1 = std::move(outp1_);
+    std::unique_ptr<const C2NumbersPortTuning> inp1 = std::move(inp1_);
+    std::shared_ptr<C2NumbersPortTuning> boutp1(C2NumbersPortTuning::alloc_shared(1)),
+            binp1 = C2NumbersPortTuning::alloc_shared(1),
+            binp3 = C2NumbersPortTuning::alloc_shared(1, false);
+    binp3->m.mNumbers[0] = 100;
+    std::unique_ptr<C2NumbersPortTuning::input> inp2_(C2NumbersPortTuning::input::alloc_unique(1));
+    inp2_->m.mNumbers[0] = 100;
+    std::unique_ptr<const C2NumbersPortTuning::input> inp2 = std::move(inp2_);
+    std::shared_ptr<C2NumbersPortTuning::input> binp2(C2NumbersPortTuning::input::alloc_shared(1));
+    std::unique_ptr<C2NumbersPortTuning::output> outp2_(C2NumbersPortTuning::output::alloc_unique(1));
+    outp2_->m.mNumbers[0] = 100;
+    std::unique_ptr<const C2NumbersPortTuning::output> outp2 = std::move(outp2_);
+    std::shared_ptr<C2NumbersPortTuning::output> boutp2(C2NumbersPortTuning::output::alloc_shared(1));
+
+    {
+        static_assert(canCallSetPort(*binp3), "should be able to");
+        static_assert(canCallSetPort(*binp1), "should be able to");
+        static_assert(!canCallSetPort(*inp1), "should not be able to (const)");
+        static_assert(!canCallSetPort(*inp2), "should not be able to (const & type)");
+        static_assert(!canCallSetPort(*binp2), "should not be able to (type)");
+
+        // flags & invariables
+        const C2NumbersPortTuning *P[] = { outp1.get(), inp1.get(), boutp1.get() };
+        for (const auto p : P) {
+            EXPECT_EQ(12u, p->size());
+            EXPECT_FALSE(p->isVendor());
+            EXPECT_TRUE(p->isFlexible());
+            EXPECT_FALSE(p->isGlobal());
+            EXPECT_FALSE(p->forStream());
+            EXPECT_TRUE(p->forPort());
+        }
+        const C2NumbersPortTuning::input *PI[] = { inp2.get(), binp2.get() };
+        for (const auto p : PI) {
+            EXPECT_EQ(12u, p->size());
+            EXPECT_FALSE(p->isVendor());
+            EXPECT_TRUE(p->isFlexible());
+            EXPECT_FALSE(p->isGlobal());
+            EXPECT_FALSE(p->forStream());
+            EXPECT_TRUE(p->forPort());
+        }
+        const C2NumbersPortTuning::output *PO[] = { outp2.get(), boutp2.get() };
+        for (const auto p : PO) {
+            EXPECT_EQ(12u, p->size());
+            EXPECT_FALSE(p->isVendor());
+            EXPECT_TRUE(p->isFlexible());
+            EXPECT_FALSE(p->isGlobal());
+            EXPECT_FALSE(p->forStream());
+            EXPECT_TRUE(p->forPort());
+        }
+
+        // port specific flags & invariables
+        EXPECT_FALSE(outp1->forInput());
+        EXPECT_TRUE(outp1->forOutput());
+
+        EXPECT_TRUE(inp1->forInput());
+        EXPECT_FALSE(inp1->forOutput());
+
+        const C2NumbersPortTuning *P2[] = { outp1.get(), inp1.get() };
+        for (const auto p : P2) {
+            EXPECT_TRUE((bool)(*p));
+            EXPECT_FALSE(!(*p));
+            EXPECT_EQ(100, p->m.mNumbers[0]);
+        }
+        for (const auto p : PO) {
+            EXPECT_TRUE((bool)(*p));
+            EXPECT_FALSE(!(*p));
+
+            EXPECT_FALSE(p->forInput());
+            EXPECT_TRUE(p->forOutput());
+        }
+        for (const auto p : PI) {
+            EXPECT_TRUE((bool)(*p));
+            EXPECT_FALSE(!(*p));
+
+            EXPECT_TRUE(p->forInput());
+            EXPECT_FALSE(p->forOutput());
+        }
+        const C2NumbersPortTuning *P3[] = { boutp1.get() };
+        for (const auto p : P3) {
+            EXPECT_FALSE((bool)(*p));
+            EXPECT_TRUE(!(*p));
+
+            EXPECT_FALSE(p->forInput());
+            EXPECT_FALSE(p->forOutput());
+            EXPECT_EQ(0, p->m.mNumbers[0]);
+        }
+
+        // values
+        EXPECT_EQ(100, inp2->m.mNumbers[0]);
+        EXPECT_EQ(100, outp2->m.mNumbers[0]);
+        EXPECT_EQ(0, binp1->m.mNumbers[0]);
+        EXPECT_EQ(0, binp2->m.mNumbers[0]);
+        EXPECT_EQ(0, boutp1->m.mNumbers[0]);
+        EXPECT_EQ(0, boutp2->m.mNumbers[0]);
+
+        EXPECT_TRUE(*inp1 != *outp1);
+        EXPECT_TRUE(*inp1 == *inp2);
+        EXPECT_TRUE(*outp1 == *outp2);
+        EXPECT_TRUE(*binp1 == *boutp1);
+        EXPECT_TRUE(*binp2 != *boutp2);
+
+        EXPECT_TRUE(*inp1 != *binp1);
+        binp1->m.mNumbers[0] = 100;
+        EXPECT_TRUE(*inp1 != *binp1);
+        binp1->setPort(false /* output */);
+        EXPECT_TRUE((bool)*binp1);
+        EXPECT_FALSE(!*binp1);
+        EXPECT_TRUE(*inp1 == *binp1);
+
+        EXPECT_TRUE(*inp2 != *binp2);
+        binp2->m.mNumbers[0] = 100;
+        EXPECT_TRUE(*inp2 == *binp2);
+
+        binp1->setPort(true /* output */);
+        EXPECT_TRUE(*outp1 == *binp1);
+
+        EXPECT_TRUE(*outp1 != *boutp1);
+        boutp1->m.mNumbers[0] = 100;
+        EXPECT_TRUE(*outp1 != *boutp1);
+        boutp1->setPort(true /* output */);
+        EXPECT_TRUE((bool)*boutp1);
+        EXPECT_FALSE(!*boutp1);
+        EXPECT_TRUE(*outp1 == *boutp1);
+
+        EXPECT_TRUE(*outp2 != *boutp2);
+        boutp2->m.mNumbers[0] = 100;
+        EXPECT_TRUE(*outp2 == *boutp2);
+
+        boutp1->setPort(false /* output */);
+        EXPECT_TRUE(*inp1 == *boutp1);
+
+        // index
+        EXPECT_EQ(C2Param::Type(inp1->type()).baseIndex(), C2NumbersStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(inp1->type()).paramIndex(), kParamIndexNumbers);
+        EXPECT_EQ(inp1->type(), C2NumbersPortTuning::input::typeIndex);
+        EXPECT_EQ(inp1->stream(), ~0u);
+
+        EXPECT_EQ(C2Param::Type(inp2->type()).baseIndex(), C2NumbersStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(inp2->type()).paramIndex(), kParamIndexNumbers);
+        EXPECT_EQ(inp2->type(), C2NumbersPortTuning::input::typeIndex);
+        EXPECT_EQ(inp2->stream(), ~0u);
+
+        EXPECT_EQ(C2Param::Type(outp1->type()).baseIndex(), C2NumbersStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(outp1->type()).paramIndex(), kParamIndexNumbers);
+        EXPECT_EQ(outp1->type(), C2NumbersPortTuning::output::typeIndex);
+        EXPECT_EQ(outp1->stream(), ~0u);
+
+        EXPECT_EQ(C2Param::Type(outp2->type()).baseIndex(), C2NumbersStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(outp2->type()).paramIndex(), kParamIndexNumbers);
+        EXPECT_EQ(outp2->type(), C2NumbersPortTuning::output::typeIndex);
+        EXPECT_EQ(outp2->stream(), ~0u);
+
+        C2Param::BaseIndex index = C2NumbersPortTuning::input::typeIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_TRUE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+
+        index = C2NumbersPortTuning::output::typeIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_TRUE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+
+        C2Param::Type type = C2NumbersPortTuning::input::typeIndex;
+        EXPECT_FALSE(type.isVendor());
+        EXPECT_TRUE(type.isFlexible());
+        EXPECT_FALSE(type.isGlobal());
+        EXPECT_TRUE(type.forInput());
+        EXPECT_FALSE(type.forOutput());
+        EXPECT_FALSE(type.forStream());
+        EXPECT_TRUE(type.forPort());
+
+        type = C2NumbersPortTuning::output::typeIndex;
+        EXPECT_FALSE(type.isVendor());
+        EXPECT_TRUE(type.isFlexible());
+        EXPECT_FALSE(type.isGlobal());
+        EXPECT_FALSE(type.forInput());
+        EXPECT_TRUE(type.forOutput());
+        EXPECT_FALSE(type.forStream());
+        EXPECT_TRUE(type.forPort());
+
+        EXPECT_EQ(C2NumbersPortTuning::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::input::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::output::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumbersTuning::From(inp1.get()), nullptr);
+        EXPECT_EQ(C2NumbersTuning::From(inp2.get()), nullptr);
+        EXPECT_EQ(C2NumbersTuning::From(outp1.get()), nullptr);
+        EXPECT_EQ(C2NumbersTuning::From(outp2.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::From(inp1.get()), inp1.get());
+        EXPECT_EQ(C2NumbersPortTuning::From(inp2.get()), (C2NumbersPortTuning*)inp2.get());
+        EXPECT_EQ(C2NumbersPortTuning::From(outp1.get()), outp1.get());
+        EXPECT_EQ(C2NumbersPortTuning::From(outp2.get()), (C2NumbersPortTuning*)outp2.get());
+        EXPECT_EQ(C2NumbersPortTuning::input::From(inp1.get()), (C2NumbersPortTuning::input*)inp1.get());
+        EXPECT_EQ(C2NumbersPortTuning::input::From(inp2.get()), inp2.get());
+        EXPECT_EQ(C2NumbersPortTuning::input::From(outp1.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::input::From(outp2.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::output::From(inp1.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::output::From(inp2.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::output::From(outp1.get()), (C2NumbersPortTuning::output*)outp1.get());
+        EXPECT_EQ(C2NumbersPortTuning::output::From(outp2.get()), outp2.get());
+        EXPECT_EQ(C2NumbersStreamTuning::From(inp1.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::From(inp2.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::From(outp1.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::From(outp2.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::input::From(inp1.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::input::From(inp2.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::input::From(outp1.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::input::From(outp2.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::output::From(inp1.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::output::From(inp2.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::output::From(outp1.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::output::From(outp2.get()), nullptr);
+
+    }
+
+    std::unique_ptr<C2NumbersStreamTuning> outs1_(C2NumbersStreamTuning::alloc_unique(1, true, 1u));
+    outs1_->m.mNumbers[0] = 100;
+    std::unique_ptr<const C2NumbersStreamTuning> outs1 = std::move(outs1_);
+    std::unique_ptr<C2NumbersStreamTuning> ins1_(C2NumbersStreamTuning::alloc_unique(1, false, 1u));
+    ins1_->m.mNumbers[0] = 100;
+    std::unique_ptr<const C2NumbersStreamTuning> ins1 = std::move(ins1_);
+    std::shared_ptr<C2NumbersStreamTuning> bouts1(C2NumbersStreamTuning::alloc_shared(1));
+    std::shared_ptr<C2NumbersStreamTuning> bins1(C2NumbersStreamTuning::alloc_shared(1));
+    std::shared_ptr<C2NumbersStreamTuning> bins3(C2NumbersStreamTuning::alloc_shared(1, false, 1u));
+    bins3->m.mNumbers[0] = 100;
+    std::unique_ptr<C2NumbersStreamTuning::input> ins2_(C2NumbersStreamTuning::input::alloc_unique(1, 1u));
+    ins2_->m.mNumbers[0] = 100;
+    std::unique_ptr<const C2NumbersStreamTuning::input> ins2 = std::move(ins2_);
+    std::shared_ptr<C2NumbersStreamTuning::input> bins2(C2NumbersStreamTuning::input::alloc_shared(1));
+    std::unique_ptr<C2NumbersStreamTuning::output> outs2_(C2NumbersStreamTuning::output::alloc_unique(1, 1u));
+    outs2_->m.mNumbers[0] = 100;
+    std::unique_ptr<const C2NumbersStreamTuning::output> outs2 = std::move(outs2_);
+    std::shared_ptr<C2NumbersStreamTuning::output> bouts2(C2NumbersStreamTuning::output::alloc_shared(1));
+
+    {
+        static_assert(canCallSetPort(*bins3), "should be able to");
+        static_assert(canCallSetPort(*bins1), "should be able to");
+        static_assert(!canCallSetPort(*ins1), "should not be able to (const)");
+        static_assert(!canCallSetPort(*ins2), "should not be able to (const & type)");
+        static_assert(!canCallSetPort(*bins2), "should not be able to (type)");
+
+        // flags & invariables
+        const C2NumbersStreamTuning *S[] = { outs1.get(), ins1.get(), bouts1.get() };
+        for (const auto p : S) {
+            EXPECT_EQ(12u, p->size());
+            EXPECT_FALSE(p->isVendor());
+            EXPECT_TRUE(p->isFlexible());
+            EXPECT_FALSE(p->isGlobal());
+            EXPECT_TRUE(p->forStream());
+            EXPECT_FALSE(p->forPort());
+        }
+        const C2NumbersStreamTuning::input *SI[] = { ins2.get(), bins2.get() };
+        for (const auto p : SI) {
+            EXPECT_EQ(12u, p->size());
+            EXPECT_FALSE(p->isVendor());
+            EXPECT_TRUE(p->isFlexible());
+            EXPECT_FALSE(p->isGlobal());
+            EXPECT_TRUE(p->forStream());
+            EXPECT_FALSE(p->forPort());
+        }
+        const C2NumbersStreamTuning::output *SO[] = { outs2.get(), bouts2.get() };
+        for (const auto p : SO) {
+            EXPECT_EQ(12u, p->size());
+            EXPECT_FALSE(p->isVendor());
+            EXPECT_TRUE(p->isFlexible());
+            EXPECT_FALSE(p->isGlobal());
+            EXPECT_TRUE(p->forStream());
+            EXPECT_FALSE(p->forPort());
+        }
+
+        // port specific flags & invariables
+        EXPECT_FALSE(outs1->forInput());
+        EXPECT_TRUE(outs1->forOutput());
+
+        EXPECT_TRUE(ins1->forInput());
+        EXPECT_FALSE(ins1->forOutput());
+
+        const C2NumbersStreamTuning *S2[] = { outs1.get(), ins1.get() };
+        for (const auto p : S2) {
+            EXPECT_TRUE((bool)(*p));
+            EXPECT_FALSE(!(*p));
+            EXPECT_EQ(100, p->m.mNumbers[0]);
+            EXPECT_EQ(1u, p->stream());
+        }
+        for (const auto p : SO) {
+            EXPECT_TRUE((bool)(*p));
+            EXPECT_FALSE(!(*p));
+
+            EXPECT_FALSE(p->forInput());
+            EXPECT_TRUE(p->forOutput());
+        }
+        for (const auto p : SI) {
+            EXPECT_TRUE((bool)(*p));
+            EXPECT_FALSE(!(*p));
+
+            EXPECT_TRUE(p->forInput());
+            EXPECT_FALSE(p->forOutput());
+        }
+        const C2NumbersStreamTuning *S3[] = { bouts1.get() };
+        for (const auto p : S3) {
+            EXPECT_FALSE((bool)(*p));
+            EXPECT_TRUE(!(*p));
+
+            EXPECT_FALSE(p->forInput());
+            EXPECT_FALSE(p->forOutput());
+            EXPECT_EQ(0, p->m.mNumbers[0]);
+        }
+
+        // values
+        EXPECT_EQ(100, ins2->m.mNumbers[0]);
+        EXPECT_EQ(100, outs2->m.mNumbers[0]);
+        EXPECT_EQ(0, bins1->m.mNumbers[0]);
+        EXPECT_EQ(0, bins2->m.mNumbers[0]);
+        EXPECT_EQ(0, bouts1->m.mNumbers[0]);
+        EXPECT_EQ(0, bouts2->m.mNumbers[0]);
+
+        EXPECT_EQ(1u, ins2->stream());
+        EXPECT_EQ(1u, outs2->stream());
+        EXPECT_EQ(0u, bins1->stream());
+        EXPECT_EQ(0u, bins2->stream());
+        EXPECT_EQ(0u, bouts1->stream());
+        EXPECT_EQ(0u, bouts2->stream());
+
+        EXPECT_TRUE(*ins1 != *outs1);
+        EXPECT_TRUE(*ins1 == *ins2);
+        EXPECT_TRUE(*outs1 == *outs2);
+        EXPECT_TRUE(*bins1 == *bouts1);
+        EXPECT_TRUE(*bins2 != *bouts2);
+
+        EXPECT_TRUE(*ins1 != *bins1);
+        bins1->m.mNumbers[0] = 100;
+        EXPECT_TRUE(*ins1 != *bins1);
+        bins1->setPort(false /* output */);
+        EXPECT_TRUE(*ins1 != *bins1);
+        bins1->setStream(1u);
+        EXPECT_TRUE(*ins1 == *bins1);
+
+        EXPECT_TRUE(*ins2 != *bins2);
+        bins2->m.mNumbers[0] = 100;
+        EXPECT_TRUE(*ins2 != *bins2);
+        bins2->setStream(1u);
+        EXPECT_TRUE(*ins2 == *bins2);
+
+        bins1->setPort(true /* output */);
+        EXPECT_TRUE(*outs1 == *bins1);
+
+        EXPECT_TRUE(*outs1 != *bouts1);
+        bouts1->m.mNumbers[0] = 100;
+        EXPECT_TRUE(*outs1 != *bouts1);
+        bouts1->setPort(true /* output */);
+        EXPECT_TRUE(*outs1 != *bouts1);
+        bouts1->setStream(1u);
+        EXPECT_TRUE(*outs1 == *bouts1);
+
+        EXPECT_TRUE(*outs2 != *bouts2);
+        bouts2->m.mNumbers[0] = 100;
+        EXPECT_TRUE(*outs2 != *bouts2);
+        bouts2->setStream(1u);
+        EXPECT_TRUE(*outs2 == *bouts2);
+
+        bouts1->setPort(false /* output */);
+        EXPECT_TRUE(*ins1 == *bouts1);
+
+        // index
+        EXPECT_EQ(C2Param::Type(ins1->type()).baseIndex(), C2NumbersStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(ins1->type()).paramIndex(), kParamIndexNumbers);
+        EXPECT_EQ(ins1->type(), C2NumbersStreamTuning::input::typeIndex);
+
+        EXPECT_EQ(C2Param::Type(ins2->type()).baseIndex(), C2NumbersStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(ins2->type()).paramIndex(), kParamIndexNumbers);
+        EXPECT_EQ(ins2->type(), C2NumbersStreamTuning::input::typeIndex);
+
+        EXPECT_EQ(C2Param::Type(outs1->type()).baseIndex(), C2NumbersStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(outs1->type()).paramIndex(), kParamIndexNumbers);
+        EXPECT_EQ(outs1->type(), C2NumbersStreamTuning::output::typeIndex);
+
+        EXPECT_EQ(C2Param::Type(outs2->type()).baseIndex(), C2NumbersStruct::baseIndex);
+        EXPECT_EQ(C2Param::Type(outs2->type()).paramIndex(), kParamIndexNumbers);
+        EXPECT_EQ(outs2->type(), C2NumbersStreamTuning::output::typeIndex);
+
+        C2Param::BaseIndex index = C2NumbersStreamTuning::input::typeIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_TRUE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+
+        index = C2NumbersStreamTuning::output::typeIndex;
+        EXPECT_FALSE(index.isVendor());
+        EXPECT_TRUE(index.isFlexible());
+        EXPECT_EQ(index.baseIndex(), kParamIndexNumbers | C2Param::BaseIndex::_kFlexibleFlag);
+        EXPECT_EQ(index.paramIndex(), kParamIndexNumbers);
+
+        C2Param::Type type = C2NumbersStreamTuning::input::typeIndex;
+        EXPECT_FALSE(type.isVendor());
+        EXPECT_TRUE(type.isFlexible());
+        EXPECT_FALSE(type.isGlobal());
+        EXPECT_TRUE(type.forInput());
+        EXPECT_FALSE(type.forOutput());
+        EXPECT_TRUE(type.forStream());
+        EXPECT_FALSE(type.forPort());
+
+        type = C2NumbersStreamTuning::output::typeIndex;
+        EXPECT_FALSE(type.isVendor());
+        EXPECT_TRUE(type.isFlexible());
+        EXPECT_FALSE(type.isGlobal());
+        EXPECT_FALSE(type.forInput());
+        EXPECT_TRUE(type.forOutput());
+        EXPECT_TRUE(type.forStream());
+        EXPECT_FALSE(type.forPort());
+
+        EXPECT_EQ(C2NumbersPortTuning::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::input::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::output::From(nullptr), nullptr);
+        EXPECT_EQ(C2NumbersTuning::From(ins1.get()), nullptr);
+        EXPECT_EQ(C2NumbersTuning::From(ins2.get()), nullptr);
+        EXPECT_EQ(C2NumbersTuning::From(outs1.get()), nullptr);
+        EXPECT_EQ(C2NumbersTuning::From(outs2.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::From(ins1.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::From(ins2.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::From(outs1.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::From(outs2.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::input::From(ins1.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::input::From(ins2.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::input::From(outs1.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::input::From(outs2.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::output::From(ins1.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::output::From(ins2.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::output::From(outs1.get()), nullptr);
+        EXPECT_EQ(C2NumbersPortTuning::output::From(outs2.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::From(ins1.get()), ins1.get());
+        EXPECT_EQ(C2NumbersStreamTuning::From(ins2.get()), (C2NumbersStreamTuning*)ins2.get());
+        EXPECT_EQ(C2NumbersStreamTuning::From(outs1.get()), outs1.get());
+        EXPECT_EQ(C2NumbersStreamTuning::From(outs2.get()), (C2NumbersStreamTuning*)outs2.get());
+        EXPECT_EQ(C2NumbersStreamTuning::input::From(ins1.get()), (C2NumbersStreamTuning::input*)ins1.get());
+        EXPECT_EQ(C2NumbersStreamTuning::input::From(ins2.get()), ins2.get());
+        EXPECT_EQ(C2NumbersStreamTuning::input::From(outs1.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::input::From(outs2.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::output::From(ins1.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::output::From(ins2.get()), nullptr);
+        EXPECT_EQ(C2NumbersStreamTuning::output::From(outs1.get()), (C2NumbersStreamTuning::output*)outs1.get());
+        EXPECT_EQ(C2NumbersStreamTuning::output::From(outs2.get()), outs2.get());
+
+    }
+
+    {
+        C2Int32Value int32Value(INT32_MIN);
+        static_assert(std::is_same<decltype(int32Value.mValue), int32_t>::value, "should be int32_t");
+        EXPECT_EQ(INT32_MIN, int32Value.mValue);
+        std::list<const C2FieldDescriptor> fields = int32Value.fieldList;
+        EXPECT_EQ(1u, fields.size());
+        EXPECT_EQ(FD::INT32, fields.cbegin()->type());
+        EXPECT_EQ(1u, fields.cbegin()->length());
+        EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+    }
+
+    {
+        C2Uint32Value uint32Value(UINT32_MAX);
+        static_assert(std::is_same<decltype(uint32Value.mValue), uint32_t>::value, "should be uint32_t");
+        EXPECT_EQ(UINT32_MAX, uint32Value.mValue);
+        std::list<const C2FieldDescriptor> fields = uint32Value.fieldList;
+        EXPECT_EQ(1u, fields.size());
+        EXPECT_EQ(FD::UINT32, fields.cbegin()->type());
+        EXPECT_EQ(1u, fields.cbegin()->length());
+        EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+    }
+
+    {
+        C2Int64Value int64Value(INT64_MIN);
+        static_assert(std::is_same<decltype(int64Value.mValue), int64_t>::value, "should be int64_t");
+        EXPECT_EQ(INT64_MIN, int64Value.mValue);
+        std::list<const C2FieldDescriptor> fields = int64Value.fieldList;
+        EXPECT_EQ(1u, fields.size());
+        EXPECT_EQ(FD::INT64, fields.cbegin()->type());
+        EXPECT_EQ(1u, fields.cbegin()->length());
+        EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+    }
+
+    {
+        C2Uint64Value uint64Value(UINT64_MAX);
+        static_assert(std::is_same<decltype(uint64Value.mValue), uint64_t>::value, "should be uint64_t");
+        EXPECT_EQ(UINT64_MAX, uint64Value.mValue);
+        std::list<const C2FieldDescriptor> fields = uint64Value.fieldList;
+        EXPECT_EQ(1u, fields.size());
+        EXPECT_EQ(FD::UINT64, fields.cbegin()->type());
+        EXPECT_EQ(1u, fields.cbegin()->length());
+        EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+    }
+
+    {
+        C2FloatValue floatValue(123.4f);
+        static_assert(std::is_same<decltype(floatValue.mValue), float>::value, "should be float");
+        EXPECT_EQ(123.4f, floatValue.mValue);
+        std::list<const C2FieldDescriptor> fields = floatValue.fieldList;
+        EXPECT_EQ(1u, fields.size());
+        EXPECT_EQ(FD::FLOAT, fields.cbegin()->type());
+        EXPECT_EQ(1u, fields.cbegin()->length());
+        EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+    }
+
+    {
+        uint8_t initValue[] = "ABCD";
+        typedef C2GlobalParam<C2Setting, C2BlobValue, 0> BlobSetting;
+        std::unique_ptr<BlobSetting> blobValue = BlobSetting::alloc_unique(6, C2ConstMemoryBlock<uint8_t>(initValue));
+        static_assert(std::is_same<decltype(blobValue->m.mValue), uint8_t[]>::value, "should be uint8_t[]");
+        EXPECT_EQ(0, memcmp(blobValue->m.mValue, "ABCD\0", 6));
+        EXPECT_EQ(6u, blobValue->flexCount());
+        std::list<const C2FieldDescriptor> fields = blobValue->fieldList;
+        EXPECT_EQ(1u, fields.size());
+        EXPECT_EQ(FD::BLOB, fields.cbegin()->type());
+        EXPECT_EQ(0u, fields.cbegin()->length());
+        EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+
+        blobValue = BlobSetting::alloc_unique(3, C2ConstMemoryBlock<uint8_t>(initValue));
+        EXPECT_EQ(0, memcmp(blobValue->m.mValue, "ABC", 3));
+        EXPECT_EQ(3u, blobValue->flexCount());
+    }
+
+    {
+        constexpr char initValue[] = "ABCD";
+        typedef C2GlobalParam<C2Setting, C2StringValue, 0> StringSetting;
+        std::unique_ptr<StringSetting> stringValue = StringSetting::alloc_unique(6, C2ConstMemoryBlock<char>(initValue));
+        stringValue = StringSetting::alloc_unique(6, initValue);
+        static_assert(std::is_same<decltype(stringValue->m.mValue), char[]>::value, "should be char[]");
+        EXPECT_EQ(0, memcmp(stringValue->m.mValue, "ABCD\0", 6));
+        EXPECT_EQ(6u, stringValue->flexCount());
+        std::list<const C2FieldDescriptor> fields = stringValue->fieldList;
+        EXPECT_EQ(1u, fields.size());
+        EXPECT_EQ(FD::STRING, fields.cbegin()->type());
+        EXPECT_EQ(0u, fields.cbegin()->length());
+        EXPECT_EQ(C2String("value"), fields.cbegin()->name());
+
+        stringValue = StringSetting::alloc_unique(3, C2ConstMemoryBlock<char>(initValue));
+        EXPECT_EQ(0, memcmp(stringValue->m.mValue, "AB", 3));
+        EXPECT_EQ(3u, stringValue->flexCount());
+
+        stringValue = StringSetting::alloc_unique(11, "initValue");
+        EXPECT_EQ(0, memcmp(stringValue->m.mValue, "initValue\0", 11));
+        EXPECT_EQ(11u, stringValue->flexCount());
+
+        stringValue = StringSetting::alloc_unique(initValue);
+        EXPECT_EQ(0, memcmp(stringValue->m.mValue, "ABCD", 5));
+        EXPECT_EQ(5u, stringValue->flexCount());
+
+        stringValue = StringSetting::alloc_unique({ 'A', 'B', 'C', 'D' });
+        EXPECT_EQ(0, memcmp(stringValue->m.mValue, "ABC", 4));
+        EXPECT_EQ(4u, stringValue->flexCount());
+    }
+
+    {
+        uint32_t videoWidth[] = { 12u, C2NumbersStreamTuning::output::typeIndex, 100 };
+        C2Param *p1 = C2Param::From(videoWidth, sizeof(videoWidth));
+        EXPECT_NE(nullptr, p1);
+        EXPECT_EQ(12u, p1->size());
+        EXPECT_EQ(C2NumbersStreamTuning::output::typeIndex, p1->type());
+
+        C2NumbersStreamTuning::output *vst = C2NumbersStreamTuning::output::From(p1);
+        EXPECT_NE(nullptr, vst);
+        if (vst) {
+            EXPECT_EQ(1u, vst->flexCount());
+            EXPECT_EQ(100, vst->m.mNumbers[0]);
+        }
+
+        p1 = C2Param::From(videoWidth, sizeof(videoWidth) + 2);
+        EXPECT_EQ(nullptr, p1);
+
+        p1 = C2Param::From(videoWidth, sizeof(videoWidth) - 2);
+        EXPECT_EQ(nullptr, p1);
+
+        p1 = C2Param::From(videoWidth, 3);
+        EXPECT_EQ(nullptr, p1);
+
+        p1 = C2Param::From(videoWidth, 0);
+        EXPECT_EQ(nullptr, p1);
+    }
+
+    {
+        uint32_t videoWidth[] = { 16u, C2NumbersPortTuning::input::typeIndex, 101, 102 };
+
+        C2Param *p1 = C2Param::From(videoWidth, sizeof(videoWidth));
+        EXPECT_NE(nullptr, p1);
+        EXPECT_EQ(16u, p1->size());
+        EXPECT_EQ(C2NumbersPortTuning::input::typeIndex, p1->type());
+
+        C2NumbersPortTuning::input *vpt = C2NumbersPortTuning::input::From(p1);
+        EXPECT_NE(nullptr, vpt);
+        if (vpt) {
+            EXPECT_EQ(2u, vpt->flexCount());
+            EXPECT_EQ(101, vpt->m.mNumbers[0]);
+            EXPECT_EQ(102, vpt->m.mNumbers[1]);
+        }
+
+        p1 = C2Param::From(videoWidth, sizeof(videoWidth) + 2);
+        EXPECT_EQ(nullptr, p1);
+
+        p1 = C2Param::From(videoWidth, sizeof(videoWidth) - 2);
+        EXPECT_EQ(nullptr, p1);
+
+        p1 = C2Param::From(videoWidth, 3);
+        EXPECT_EQ(nullptr, p1);
+
+        p1 = C2Param::From(videoWidth, 0);
+        EXPECT_EQ(nullptr, p1);
+    }
+}
+
+// ***********************
+
+}
+
+#include <util/C2ParamUtils.h>
+#include <C2Config.h>
+#include <C2Component.h>
+#include <unordered_map>
+
+namespace android {
+
+C2ENUM(
+    MetadataType, int32_t,
+    kInvalid = -1,
+    kNone = 0,
+    kGralloc,
+    kNativeHandle,
+    kANativeWindow,
+    kCamera,
+)
+
+enum {
+    kParamIndexVideoConfig = 0x1234,
+};
+
+struct C2VideoConfigStruct {
+    int32_t mWidth;
+    uint32_t mHeight;
+    MetadataType mMetadataType;
+    int32_t mSupportedFormats[];
+
+    C2VideoConfigStruct() {}
+
+    DEFINE_AND_DESCRIBE_FLEX_C2STRUCT(VideoConfig, mSupportedFormats)
+    C2FIELD(mWidth, "width")
+    C2FIELD(mHeight, "height")
+    C2FIELD(mMetadataType, "metadata-type")
+    C2FIELD(mSupportedFormats, "formats")
+};
+
+typedef C2PortParam<C2Tuning, C2VideoConfigStruct> C2VideoConfigPortTuning;
+
+class MyReflector : public C2ParamReflector {
+private:
+    std::unique_ptr<C2VideoConfigPortTuning::input> inputVideoConfigTuning;
+    std::unique_ptr<C2VideoConfigPortTuning::output> outputVideoConfigTuning;
+
+public:
+    void describeSupportedValues() {
+        C2TypedFieldSupportedValues<int32_t> supportedWidths(16, 1920, 8);
+        C2FieldSupportedValues supportedWidths2(16, 1920, 8);
+
+
+        std::list<C2FieldSupportedValues> supported;
+        //supported.emplace_push(inputVideoConfigTuning->mNumber, range(16, 1920, 8));
+        //supported.emplace_push(inputVideoConfigTuning->mHeight, range(16, 1088, 8));
+        //supported.emplace_push(inputVideoConfigTuning->mMetadataType, all_enums);
+        //supported.emplace_push(inputVideoConfigTuning->mSupportedFormats, { 0, 1, 5, 7 });
+    }
+
+    virtual std::unique_ptr<android::C2StructDescriptor> describe(C2Param::BaseIndex paramType) {
+        switch (paramType.baseIndex()) {
+        case C2VideoConfigPortTuning::baseIndex:
+            return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor{
+                paramType.baseIndex(),
+                C2VideoConfigPortTuning::fieldList,
+            });
+        }
+        return nullptr;
+    }
+};
+
+class MyComponentInstance : public C2ComponentInterface {
+public:
+    virtual C2String getName() const {
+        /// \todo this seems too specific
+        return "sample.interface";
+    };
+
+    virtual node_id getId() const {
+        /// \todo how are these shared?
+        return 0;
+    }
+
+    virtual status_t commit_sm(
+            const std::vector<C2Param* const> &params,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+        (void)params;
+        (void)failures;
+        return C2_UNSUPPORTED;
+    }
+
+    virtual status_t config_nb(
+            const std::vector<C2Param* const> &params,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+        (void)params;
+        (void)failures;
+        return C2_UNSUPPORTED;
+    }
+
+    virtual status_t createTunnel_sm(node_id targetComponent) {
+        (void)targetComponent;
+        return C2_UNSUPPORTED;
+    }
+
+    virtual status_t query_nb(
+            const std::vector<C2Param* const> &stackParams,
+            const std::vector<C2Param::Index> &heapParamIndices,
+            std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
+        for (C2Param* const param : stackParams) {
+            if (!*param) { // param is already invalid - remember it
+                continue;
+            }
+
+            // note: this does not handle stream params (should use index...)
+            if (!mMyParams.count(param->type())) {
+                continue; // not my param
+            }
+
+            C2Param & myParam = mMyParams.find(param->type())->second;
+            if (myParam.size() != param->size()) { // incorrect size
+                param->invalidate();
+                continue;
+            }
+
+            param->updateFrom(myParam);
+        }
+
+        for (const C2Param::Index index : heapParamIndices) {
+            if (mMyParams.count(index)) {
+                C2Param & myParam = mMyParams.find(index)->second;
+                std::unique_ptr<C2Param> paramCopy(C2Param::From(&myParam, myParam.size()));
+                heapParams->push_back(std::move(paramCopy));
+            }
+        }
+
+        return C2_OK;
+    }
+
+    std::unordered_map<uint32_t, C2Param &> mMyParams;
+
+    C2ComponentDomainInfo mDomainInfo;
+
+    MyComponentInstance() {
+        mMyParams.insert({mDomainInfo.type(), mDomainInfo});
+    }
+
+    virtual status_t releaseTunnel_sm(node_id targetComponent) {
+        (void)targetComponent;
+        return C2_UNSUPPORTED;
+    }
+
+    class MyParamReflector : public C2ParamReflector {
+        const MyComponentInstance *instance;
+
+    public:
+        MyParamReflector(const MyComponentInstance *i) : instance(i) { }
+
+        virtual std::unique_ptr<C2StructDescriptor> describe(C2Param::BaseIndex paramIndex) {
+            switch (paramIndex.baseIndex()) {
+            case decltype(instance->mDomainInfo)::baseIndex:
+            default:
+                return std::unique_ptr<C2StructDescriptor>(new C2StructDescriptor{
+                    instance->mDomainInfo.type(),
+                    decltype(instance->mDomainInfo)::fieldList,
+                });
+            }
+            return nullptr;
+        }
+    };
+
+    virtual status_t getSupportedValues(
+            const std::vector<const C2ParamField> fields,
+            std::vector<C2FieldSupportedValues>* const values) const {
+        for (const C2ParamField &field : fields) {
+            if (field == C2ParamField(&mDomainInfo, &C2ComponentDomainInfo::mValue)) {
+                values->push_back(C2FieldSupportedValues(
+                    false /* flag */,
+                    &mDomainInfo.mValue
+                    //,
+                    //{(int32_t)C2DomainVideo}
+                ));
+            }
+        }
+        return C2_OK;
+    }
+
+    virtual std::shared_ptr<C2ParamReflector> getParamReflector() const {
+        return std::shared_ptr<C2ParamReflector>(new MyParamReflector(this));
+    }
+
+    virtual status_t getSupportedParams(std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const {
+        params->push_back(std::make_shared<C2ParamDescriptor>(
+                true /* required */, "_domain", &mDomainInfo));
+        return C2_OK;
+    }
+
+    status_t getSupportedParams2(std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) {
+        params->push_back(std::shared_ptr<C2ParamDescriptor>(
+                new C2ParamDescriptor(true /* required */, "_domain", &mDomainInfo)));
+        return C2_OK;
+    }
+
+};
+
+template<typename E, bool S=std::is_enum<E>::value>
+struct getter {
+    int32_t get(const C2FieldSupportedValues::Primitive &p, int32_t*) {
+        return p.i32;
+    }
+    int64_t get(const C2FieldSupportedValues::Primitive &p, int64_t*) {
+        return p.i64;
+    }
+    uint32_t get(const C2FieldSupportedValues::Primitive &p, uint32_t*) {
+        return p.u32;
+    }
+    uint64_t get(const C2FieldSupportedValues::Primitive &p, uint64_t*) {
+        return p.u64;
+    }
+    float get(const C2FieldSupportedValues::Primitive &p, float*) {
+        return p.fp;
+    }
+};
+
+template<typename E>
+struct getter<E, true> {
+     typename std::underlying_type<E>::type get(const C2FieldSupportedValues::Primitive &p, E*) {
+         using u=typename std::underlying_type<E>::type;
+         return getter<u>().get(p, (u*)0);
+     }
+};
+
+template<typename T, bool E=std::is_enum<T>::value>
+struct lax_underlying_type {
+    typedef typename std::underlying_type<T>::type type;
+};
+
+template<typename T>
+struct lax_underlying_type<T, false> {
+    typedef T type;
+};
+
+template<typename E>
+typename lax_underlying_type<E>::type get(
+        const C2FieldSupportedValues::Primitive &p, E*) {
+    return getter<E>().get(p, (E*)0);
+}
+
+template<typename T>
+void dumpFSV(const C2FieldSupportedValues &sv, T*t) {
+    using namespace std;
+    cout << (std::is_enum<T>::value ? (std::is_signed<typename std::underlying_type<T>::type>::value ? "i" : "u")
+             : std::is_integral<T>::value ? std::is_signed<T>::value ? "i" : "u" : "f")
+        << (8 * sizeof(T));
+    if (sv.type == sv.RANGE) {
+        cout << ".range(" << get(sv.range.min, t);
+        if (get(sv.range.step, t) != std::is_integral<T>::value) {
+            cout << ":" << get(sv.range.step, t);
+        }
+        if (get(sv.range.nom, t) != 1 || get(sv.range.denom, t) != 1) {
+            cout << ":" << get(sv.range.nom, t) << "/" << get(sv.range.denom, t);
+        }
+        cout << get(sv.range.max, t) << ")";
+    }
+    if (sv.values.size()) {
+        cout << (sv.type == sv.FLAGS ? ".flags(" : ".list(");
+        const char *sep = "";
+        for (const C2FieldSupportedValues::Primitive &p : sv.values) {
+            cout << sep << get(p, t);
+            sep = ",";
+        }
+        cout << ")";
+    }
+    cout << endl;
+}
+
+void dumpType(C2Param::Type type) {
+    using namespace std;
+    cout << (type.isVendor() ? "Vendor" : "C2");
+    if (type.forInput()) {
+        cout << "Input";
+    } else if (type.forOutput()) {
+        cout << "Output";
+    } else if (type.forPort() && !type.forStream()) {
+        cout << "Port";
+    }
+    if (type.forStream()) {
+        cout << "Stream";
+    }
+
+    if (type.isFlexible()) {
+        cout << "Flex";
+    }
+
+    cout << type.paramIndex();
+
+    switch (type.kind()) {
+    case C2Param::INFO: cout << "Info"; break;
+    case C2Param::SETTING: cout << "Setting"; break;
+    case C2Param::TUNING: cout << "Tuning"; break;
+    case C2Param::STRUCT: cout << "Struct"; break;
+    default: cout << "Kind" << (int32_t)type.kind(); break;
+    }
+}
+
+void dumpType(C2Param::BaseIndex type) {
+    using namespace std;
+    cout << (type.isVendor() ? "Vendor" : "C2");
+    if (type.isFlexible()) {
+        cout << "Flex";
+    }
+
+    cout << type.paramIndex() << "Struct";
+}
+
+void dumpType(FD::Type type) {
+    using namespace std;
+    switch (type) {
+    case FD::BLOB: cout << "blob "; break;
+    case FD::FLOAT: cout << "float "; break;
+    case FD::INT32: cout << "int32_t "; break;
+    case FD::INT64: cout << "int64_t "; break;
+    case FD::UINT32: cout << "uint32_t "; break;
+    case FD::UINT64: cout << "uint64_t "; break;
+    case FD::STRING: cout << "char "; break;
+    default:
+        cout << "struct ";
+        dumpType((C2Param::Type)type);
+        break;
+    }
+}
+
+void dumpStruct(const C2StructDescriptor &sd) {
+    using namespace std;
+    cout << "struct ";
+    dumpType(sd.baseIndex());
+    cout << " {" << endl;
+    //C2FieldDescriptor &f;
+    for (const C2FieldDescriptor &f : sd) {
+        PrintTo(f, &cout);
+        cout << endl;
+
+        if (f.namedValues().size()) {
+            cout << ".named(";
+            const char *sep = "";
+            for (const FD::named_value_type &p : f.namedValues()) {
+                cout << sep << p.first << "=";
+                switch (f.type()) {
+                case C2Value::INT32: cout << get(p.second, (int32_t *)0); break;
+                case C2Value::INT64: cout << get(p.second, (int64_t *)0); break;
+                case C2Value::UINT32: cout << get(p.second, (uint32_t *)0); break;
+                case C2Value::UINT64: cout << get(p.second, (uint64_t *)0); break;
+                case C2Value::FLOAT: cout << get(p.second, (float *)0); break;
+                default: cout << "???"; break;
+                }
+                sep = ",";
+            }
+            cout << ")";
+        }
+    }
+
+    cout << "};" << endl;
+}
+
+void dumpDesc(const C2ParamDescriptor &pd) {
+    using namespace std;
+    if (pd.isRequired()) {
+        cout << "required ";
+    }
+    if (pd.isPersistent()) {
+        cout << "persistent ";
+    }
+    cout << "struct ";
+    dumpType(pd.type());
+    cout << " " << pd.name() << ";" << endl;
+}
+
+TEST_F(C2ParamTest, ReflectorTest) {
+    C2ComponentDomainInfo domainInfo;
+    std::shared_ptr<C2ComponentInterface> comp(new MyComponentInstance);
+    std::vector<C2FieldSupportedValues> values;
+
+    std::unique_ptr<C2StructDescriptor> desc{
+        comp->getParamReflector()->describe(C2ComponentDomainInfo::indexFlags)};
+    dumpStruct(*desc);
+
+    EXPECT_EQ(
+        C2_OK,
+        comp->getSupportedValues(
+            { C2ParamField(&domainInfo, &C2ComponentDomainInfo::mValue) },
+            &values)
+    );
+
+    for (const C2FieldSupportedValues &sv : values) {
+        dumpFSV(sv, &domainInfo.mValue);
+    }
+}
+
+C2ENUM(Enum1, uint32_t,
+    Enum1Value1,
+    Enum1Value2,
+    Enum1Value4 = Enum1Value2 + 2,
+);
+
+C2ENUM_CUSTOM_PREFIX(Enum2, uint32_t, "Enum",
+    Enum2Value1,
+    Enum2Value2,
+    Enum2Value4 = Enum1Value2 + 2,
+);
+
+C2ENUM_CUSTOM_NAMES(Enum3, uint8_t,
+    ({ { "value1", Enum3Value1 },
+       { "value2", Enum3Value2 },
+       { "value4", Enum3Value4 },
+       { "invalid", Invalid } }),
+    Enum3Value1,
+    Enum3Value2,
+    Enum3Value4 = Enum3Value2 + 2,
+    Invalid,
+);
+
+TEST_F(C2ParamTest, EnumUtilsTest) {
+    std::vector<std::pair<C2String, Enum3>> pairs ( { { "value1", Enum3Value1 },
+      { "value2", Enum3Value2 },
+      { "value4", Enum3Value4 },
+      { "invalid", Invalid } });
+    Enum3 e3;
+    FD::namedValuesFor(e3);
+}
+
+TEST_F(C2ParamTest, ParamUtilsTest) {
+    // upper case
+    EXPECT_EQ("yes", C2ParamUtils::camelCaseToDashed("YES"));
+    EXPECT_EQ("no", C2ParamUtils::camelCaseToDashed("NO"));
+    EXPECT_EQ("yes-no", C2ParamUtils::camelCaseToDashed("YES_NO"));
+    EXPECT_EQ("yes-no", C2ParamUtils::camelCaseToDashed("YES__NO"));
+    EXPECT_EQ("a2dp", C2ParamUtils::camelCaseToDashed("A2DP"));
+    EXPECT_EQ("mp2-ts", C2ParamUtils::camelCaseToDashed("MP2_TS"));
+    EXPECT_EQ("block-2d", C2ParamUtils::camelCaseToDashed("BLOCK_2D"));
+    EXPECT_EQ("mpeg-2-ts", C2ParamUtils::camelCaseToDashed("MPEG_2_TS"));
+    EXPECT_EQ("_hidden-value", C2ParamUtils::camelCaseToDashed("_HIDDEN_VALUE"));
+    EXPECT_EQ("__hidden-value2", C2ParamUtils::camelCaseToDashed("__HIDDEN_VALUE2"));
+    EXPECT_EQ("__hidden-value-2", C2ParamUtils::camelCaseToDashed("__HIDDEN_VALUE_2"));
+
+    // camel case
+    EXPECT_EQ("yes", C2ParamUtils::camelCaseToDashed("Yes"));
+    EXPECT_EQ("no", C2ParamUtils::camelCaseToDashed("No"));
+    EXPECT_EQ("yes-no", C2ParamUtils::camelCaseToDashed("YesNo"));
+    EXPECT_EQ("yes-no", C2ParamUtils::camelCaseToDashed("Yes_No"));
+    EXPECT_EQ("mp2-ts", C2ParamUtils::camelCaseToDashed("MP2Ts"));
+    EXPECT_EQ("block-2d", C2ParamUtils::camelCaseToDashed("Block2D"));
+    EXPECT_EQ("mpeg-2-ts", C2ParamUtils::camelCaseToDashed("Mpeg2ts"));
+    EXPECT_EQ("_hidden-value", C2ParamUtils::camelCaseToDashed("_HiddenValue"));
+    EXPECT_EQ("__hidden-value-2", C2ParamUtils::camelCaseToDashed("__HiddenValue2"));
+
+    // mixed case
+    EXPECT_EQ("mp2t-s", C2ParamUtils::camelCaseToDashed("MP2T_s"));
+    EXPECT_EQ("block-2d", C2ParamUtils::camelCaseToDashed("Block_2D"));
+    EXPECT_EQ("block-2-d", C2ParamUtils::camelCaseToDashed("Block2_D"));
+    EXPECT_EQ("mpeg-2-ts", C2ParamUtils::camelCaseToDashed("Mpeg_2ts"));
+    EXPECT_EQ("mpeg-2-ts", C2ParamUtils::camelCaseToDashed("Mpeg_2_TS"));
+    EXPECT_EQ("_hidden-value", C2ParamUtils::camelCaseToDashed("_Hidden__VALUE"));
+    EXPECT_EQ("__hidden-value-2", C2ParamUtils::camelCaseToDashed("__HiddenValue_2"));
+    EXPECT_EQ("_2", C2ParamUtils::camelCaseToDashed("_2"));
+    EXPECT_EQ("__23", C2ParamUtils::camelCaseToDashed("__23"));
+}
+
+TEST_F(C2ParamTest, C2ValueTest) {
+    C2Value val;
+    int32_t i32 = -32;
+    int64_t i64 = -64;
+    uint32_t u32 = 32;
+    uint64_t u64 = 64;
+    float fp = 1.5f;
+
+    EXPECT_EQ(C2Value::NO_INIT, val.type());
+    EXPECT_EQ(false, val.get(&i32));
+    EXPECT_EQ(-32, i32);
+    EXPECT_EQ(false, val.get(&i64));
+    EXPECT_EQ(-64, i64);
+    EXPECT_EQ(false, val.get(&u32));
+    EXPECT_EQ(32u, u32);
+    EXPECT_EQ(false, val.get(&u64));
+    EXPECT_EQ(64u, u64);
+    EXPECT_EQ(false, val.get(&fp));
+    EXPECT_EQ(1.5f, fp);
+
+    val = int32_t(-3216);
+    EXPECT_EQ(C2Value::INT32, val.type());
+    EXPECT_EQ(true, val.get(&i32));
+    EXPECT_EQ(-3216, i32);
+    EXPECT_EQ(false, val.get(&i64));
+    EXPECT_EQ(-64, i64);
+    EXPECT_EQ(false, val.get(&u32));
+    EXPECT_EQ(32u, u32);
+    EXPECT_EQ(false, val.get(&u64));
+    EXPECT_EQ(64u, u64);
+    EXPECT_EQ(false, val.get(&fp));
+    EXPECT_EQ(1.5f, fp);
+
+    val = uint32_t(3216);
+    EXPECT_EQ(C2Value::UINT32, val.type());
+    EXPECT_EQ(false, val.get(&i32));
+    EXPECT_EQ(-3216, i32);
+    EXPECT_EQ(false, val.get(&i64));
+    EXPECT_EQ(-64, i64);
+    EXPECT_EQ(true, val.get(&u32));
+    EXPECT_EQ(3216u, u32);
+    EXPECT_EQ(false, val.get(&u64));
+    EXPECT_EQ(64u, u64);
+    EXPECT_EQ(false, val.get(&fp));
+    EXPECT_EQ(1.5f, fp);
+
+    val = int64_t(-6432);
+    EXPECT_EQ(C2Value::INT64, val.type());
+    EXPECT_EQ(false, val.get(&i32));
+    EXPECT_EQ(-3216, i32);
+    EXPECT_EQ(true, val.get(&i64));
+    EXPECT_EQ(-6432, i64);
+    EXPECT_EQ(false, val.get(&u32));
+    EXPECT_EQ(3216u, u32);
+    EXPECT_EQ(false, val.get(&u64));
+    EXPECT_EQ(64u, u64);
+    EXPECT_EQ(false, val.get(&fp));
+    EXPECT_EQ(1.5f, fp);
+
+    val = uint64_t(6432);
+    EXPECT_EQ(C2Value::UINT64, val.type());
+    EXPECT_EQ(false, val.get(&i32));
+    EXPECT_EQ(-3216, i32);
+    EXPECT_EQ(false, val.get(&i64));
+    EXPECT_EQ(-6432, i64);
+    EXPECT_EQ(false, val.get(&u32));
+    EXPECT_EQ(3216u, u32);
+    EXPECT_EQ(true, val.get(&u64));
+    EXPECT_EQ(6432u, u64);
+    EXPECT_EQ(false, val.get(&fp));
+    EXPECT_EQ(1.5f, fp);
+
+    val = 15.25f;
+    EXPECT_EQ(C2Value::FLOAT, val.type());
+    EXPECT_EQ(false, val.get(&i32));
+    EXPECT_EQ(-3216, i32);
+    EXPECT_EQ(false, val.get(&i64));
+    EXPECT_EQ(-6432, i64);
+    EXPECT_EQ(false, val.get(&u32));
+    EXPECT_EQ(3216u, u32);
+    EXPECT_EQ(false, val.get(&u64));
+    EXPECT_EQ(6432u, u64);
+    EXPECT_EQ(true, val.get(&fp));
+    EXPECT_EQ(15.25f, fp);
+}
+
+} // namespace android
diff --git a/media/libstagefright/codec2/tests/C2_test.cpp b/media/libstagefright/codec2/tests/C2_test.cpp
new file mode 100644
index 0000000..92a3d91
--- /dev/null
+++ b/media/libstagefright/codec2/tests/C2_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "C2_test"
+
+#include <gtest/gtest.h>
+
+#include <C2.h>
+
+namespace android {
+
+/* ======================================= STATIC TESTS ======================================= */
+
+template<int N>
+struct c2_const_checker
+{
+    inline constexpr static int num() { return N; }
+};
+
+constexpr auto min_i32_i32 = c2_min(int32_t(1), int32_t(2));
+static_assert(std::is_same<decltype(min_i32_i32), const int32_t>::value, "should be int32_t");
+constexpr auto min_i32_i64 = c2_min(int32_t(3), int64_t(2));
+static_assert(std::is_same<decltype(min_i32_i64), const int64_t>::value, "should be int64_t");
+constexpr auto min_i8_i32 = c2_min(int8_t(0xff), int32_t(0xffffffff));
+static_assert(std::is_same<decltype(min_i8_i32), const int32_t>::value, "should be int32_t");
+
+static_assert(c2_const_checker<min_i32_i32>::num() == 1, "should be 1");
+static_assert(c2_const_checker<min_i32_i64>::num() == 2, "should be 2");
+static_assert(c2_const_checker<min_i8_i32>::num() == 0xffffffff, "should be 0xffffffff");
+
+constexpr auto min_u32_u32 = c2_min(uint32_t(1), uint32_t(2));
+static_assert(std::is_same<decltype(min_u32_u32), const uint32_t>::value, "should be uint32_t");
+constexpr auto min_u32_u64 = c2_min(uint32_t(3), uint64_t(2));
+static_assert(std::is_same<decltype(min_u32_u64), const uint32_t>::value, "should be uint32_t");
+constexpr auto min_u32_u8 = c2_min(uint32_t(0xffffffff), uint8_t(0xff));
+static_assert(std::is_same<decltype(min_u32_u8), const uint8_t>::value, "should be uint8_t");
+
+static_assert(c2_const_checker<min_u32_u32>::num() == 1, "should be 1");
+static_assert(c2_const_checker<min_u32_u64>::num() == 2, "should be 2");
+static_assert(c2_const_checker<min_u32_u8>::num() == 0xff, "should be 0xff");
+
+constexpr auto max_i32_i32 = c2_max(int32_t(1), int32_t(2));
+static_assert(std::is_same<decltype(max_i32_i32), const int32_t>::value, "should be int32_t");
+constexpr auto max_i32_i64 = c2_max(int32_t(3), int64_t(2));
+static_assert(std::is_same<decltype(max_i32_i64), const int64_t>::value, "should be int64_t");
+constexpr auto max_i8_i32 = c2_max(int8_t(0xff), int32_t(0xffffffff));
+static_assert(std::is_same<decltype(max_i8_i32), const int32_t>::value, "should be int32_t");
+
+static_assert(c2_const_checker<max_i32_i32>::num() == 2, "should be 2");
+static_assert(c2_const_checker<max_i32_i64>::num() == 3, "should be 3");
+static_assert(c2_const_checker<max_i8_i32>::num() == 0xffffffff, "should be 0xffffffff");
+
+constexpr auto max_u32_u32 = c2_max(uint32_t(1), uint32_t(2));
+static_assert(std::is_same<decltype(max_u32_u32), const uint32_t>::value, "should be uint32_t");
+constexpr auto max_u32_u64 = c2_max(uint32_t(3), uint64_t(2));
+static_assert(std::is_same<decltype(max_u32_u64), const uint64_t>::value, "should be uint64_t");
+constexpr auto max_u32_u8 = c2_max(uint32_t(0x7fffffff), uint8_t(0xff));
+static_assert(std::is_same<decltype(max_u32_u8), const uint32_t>::value, "should be uint32_t");
+
+static_assert(c2_const_checker<max_u32_u32>::num() == 2, "should be 2");
+static_assert(c2_const_checker<max_u32_u64>::num() == 3, "should be 3");
+static_assert(c2_const_checker<max_u32_u8>::num() == 0x7fffffff, "should be 0x7fffffff");
+
+} // namespace android
diff --git a/media/libstagefright/codec2/tests/vndk/C2UtilTest.cpp b/media/libstagefright/codec2/tests/vndk/C2UtilTest.cpp
new file mode 100644
index 0000000..7a1374b
--- /dev/null
+++ b/media/libstagefright/codec2/tests/vndk/C2UtilTest.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <util/_C2MacroUtils.h>
+
+/** \file
+ * Tests for vndk/util.
+ */
+
+/* --------------------------------------- _C2MacroUtils --------------------------------------- */
+
+static_assert(0 == _C2_ARGC(), "should be 0");
+static_assert(1 == _C2_ARGC(1), "should be 1");
+static_assert(2 == _C2_ARGC(1, 2), "should be 2");
+static_assert(64 == _C2_ARGC(
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+        26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+        49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64), "should be 64");
+
+static_assert(0 == _C2_ARGC(,), "should be 0");
+static_assert(1 == _C2_ARGC(1,), "should be 1");
+static_assert(2 == _C2_ARGC(1, 2,), "should be 2");
+static_assert(64 == _C2_ARGC(
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+        26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+        49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,), "should be 64");
+
diff --git a/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
new file mode 100644
index 0000000..edae303
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/include/util/C2ParamUtils.h
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2UTILS_PARAM_UTILS_H_
+#define C2UTILS_PARAM_UTILS_H_
+
+#include <C2Param.h>
+#include <util/_C2MacroUtils.h>
+
+#include <iostream>
+
+/** \file
+ * Utilities for parameter handling to be used by Codec2 implementations.
+ */
+
+namespace android {
+
+/// \cond INTERNAL
+
+/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
+
+/**
+ * Utility class that allows ignoring enum value assignment (e.g. both '(_C2EnumConst)kValue = x'
+ * and '(_C2EnumConst)kValue' will eval to kValue.
+ */
+template<typename T>
+class _C2EnumConst {
+public:
+    // implicit conversion from T
+    inline _C2EnumConst(T value) : _mValue(value) {}
+    // implicit conversion to T
+    inline operator T() { return _mValue; }
+    // implicit conversion to C2Value::Primitive
+    inline operator C2Value::Primitive() { return (T)_mValue; }
+    // ignore assignment and return T here to avoid implicit conversion to T later
+    inline T &operator =(T value __unused) { return _mValue; }
+private:
+    T _mValue;
+};
+
+/// mapper to get name of enum
+/// \note this will contain any initialization, which we will remove when converting to lower-case
+#define _C2_GET_ENUM_NAME(x, y) #x
+/// mapper to get value of enum
+#define _C2_GET_ENUM_VALUE(x, type) (_C2EnumConst<type>)x
+
+/// \endcond
+
+#define DEFINE_C2_ENUM_VALUE_AUTO_HELPER(name, type, prefix, ...) \
+template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
+    return C2ParamUtils::sanitizeEnumValues( \
+            std::vector<C2Value::Primitive> { _C2_MAP(_C2_GET_ENUM_VALUE, type, __VA_ARGS__) }, \
+            { _C2_MAP(_C2_GET_ENUM_NAME, type, __VA_ARGS__) }, \
+            prefix); \
+}
+
+#define DEFINE_C2_ENUM_VALUE_CUSTOM_HELPER(name, type, names, ...) \
+template<> C2FieldDescriptor::named_values_type C2FieldDescriptor::namedValuesFor(const name &r __unused) { \
+    return C2ParamUtils::customEnumValues( \
+            std::vector<std::pair<C2StringLiteral, name>> names); \
+}
+
+
+class C2ParamUtils {
+private:
+    static size_t countLeadingUnderscores(C2StringLiteral a) {
+        size_t i = 0;
+        while (a[i] == '_') {
+            ++i;
+        }
+        return i;
+    }
+
+    static size_t countMatching(C2StringLiteral a, const C2String &b) {
+        for (size_t i = 0; i < b.size(); ++i) {
+            if (!a[i] || a[i] != b[i]) {
+                return i;
+            }
+        }
+        return b.size();
+    }
+
+    // ABCDef => abc-def
+    // ABCD2ef => abcd2-ef // 0
+    // ABCD2Ef => ancd2-ef // -1
+    // AbcDef => abc-def // -1
+    // Abc2Def => abc-2def
+    // Abc2def => abc-2-def
+    // _Yo => _yo
+    // _yo => _yo
+    // C2_yo => c2-yo
+    // C2__yo => c2-yo
+
+    static C2String camelCaseToDashed(C2String name) {
+        enum {
+            kNone = '.',
+            kLower = 'a',
+            kUpper = 'A',
+            kDigit = '1',
+            kDash = '-',
+            kUnderscore = '_',
+        } type = kNone;
+        size_t word_start = 0;
+        for (size_t ix = 0; ix < name.size(); ++ix) {
+            /* std::cout << name.substr(0, word_start) << "|"
+                    << name.substr(word_start, ix - word_start) << "["
+                    << name.substr(ix, 1) << "]" << name.substr(ix + 1)
+                    << ": " << (char)type << std::endl; */
+            if (isupper(name[ix])) {
+                if (type == kLower) {
+                    name.insert(ix++, 1, '-');
+                    word_start = ix;
+                }
+                name[ix] = tolower(name[ix]);
+                type = kUpper;
+            } else if (islower(name[ix])) {
+                if (type == kDigit && ix > 0) {
+                    name.insert(ix++, 1, '-');
+                    word_start = ix;
+                } else if (type == kUpper && ix > word_start + 1) {
+                    name.insert(ix++ - 1, 1, '-');
+                    word_start = ix - 1;
+                }
+                type = kLower;
+            } else if (isdigit(name[ix])) {
+                if (type == kLower) {
+                    name.insert(ix++, 1, '-');
+                    word_start = ix;
+                }
+                type = kDigit;
+            } else if (name[ix] == '_') {
+                if (type == kDash) {
+                    name.erase(ix--, 1);
+                } else if (type != kNone && type != kUnderscore) {
+                    name[ix] = '-';
+                    type = kDash;
+                    word_start = ix + 1;
+                } else {
+                    type = kUnderscore;
+                    word_start = ix + 1;
+                }
+            } else {
+                name.resize(ix);
+            }
+        }
+        // std::cout << "=> " << name << std::endl;
+        return name;
+    }
+
+    static std::vector<C2String> sanitizeEnumValueNames(
+            const std::vector<C2StringLiteral> names,
+            C2StringLiteral _prefix = NULL) {
+        std::vector<C2String> sanitizedNames;
+        C2String prefix;
+        size_t extraUnderscores = 0;
+        bool first = true;
+        if (_prefix) {
+            extraUnderscores = countLeadingUnderscores(_prefix);
+            prefix = _prefix + extraUnderscores;
+            first = false;
+            // std::cout << "prefix:" << prefix << ", underscores:" << extraUnderscores << std::endl;
+        }
+
+        // calculate prefix and minimum leading underscores
+        for (C2StringLiteral s : names) {
+            // std::cout << s << std::endl;
+            size_t underscores = countLeadingUnderscores(s);
+            if (first) {
+                extraUnderscores = underscores;
+                prefix = s + underscores;
+                first = false;
+            } else {
+                size_t matching = countMatching(
+                    s + underscores,
+                    prefix);
+                prefix.resize(matching);
+                extraUnderscores = std::min(underscores, extraUnderscores);
+            }
+            // std::cout << "prefix:" << prefix << ", underscores:" << extraUnderscores << std::endl;
+            if (prefix.size() == 0 && extraUnderscores == 0) {
+                break;
+            }
+        }
+
+        // we swallow the first underscore after upper case prefixes
+        bool upperCasePrefix = true;
+        for (size_t i = 0; i < prefix.size(); ++i) {
+            if (islower(prefix[i])) {
+                upperCasePrefix = false;
+                break;
+            }
+        }
+
+        for (C2StringLiteral s : names) {
+            size_t underscores = countLeadingUnderscores(s);
+            C2String sanitized = C2String(s, underscores - extraUnderscores);
+            sanitized.append(s + prefix.size() + underscores +
+                        (upperCasePrefix && s[prefix.size() + underscores] == '_'));
+            sanitizedNames.push_back(camelCaseToDashed(sanitized));
+        }
+
+        for (C2String s : sanitizedNames) {
+            std::cout << s << std::endl;
+        }
+
+        return sanitizedNames;
+    }
+
+    friend class C2ParamTest_ParamUtilsTest_Test;
+
+public:
+    static std::vector<C2String> getEnumValuesFromString(C2StringLiteral value) {
+        std::vector<C2String> foundNames;
+        size_t pos = 0, len = strlen(value);
+        do {
+            size_t endPos = strcspn(value + pos, " ,=") + pos;
+            if (endPos > pos) {
+                foundNames.emplace_back(value + pos, endPos - pos);
+            }
+            if (value[endPos] && value[endPos] != ',') {
+                endPos += strcspn(value + endPos, ",");
+            }
+            pos = strspn(value + endPos, " ,") + endPos;
+        } while (pos < len);
+        return foundNames;
+    }
+
+    template<typename T>
+    static C2FieldDescriptor::named_values_type sanitizeEnumValues(
+            std::vector<T> values,
+            std::vector<C2StringLiteral> names,
+            C2StringLiteral prefix = NULL) {
+        C2FieldDescriptor::named_values_type namedValues;
+        std::vector<C2String> sanitizedNames = sanitizeEnumValueNames(names, prefix);
+        for (size_t i = 0; i < values.size() && i < sanitizedNames.size(); ++i) {
+            namedValues.emplace_back(sanitizedNames[i], values[i]);
+        }
+        return namedValues;
+    }
+
+    template<typename E>
+    static C2FieldDescriptor::named_values_type customEnumValues(
+            std::vector<std::pair<C2StringLiteral, E>> items) {
+        C2FieldDescriptor::named_values_type namedValues;
+        for (auto &item : items) {
+            namedValues.emplace_back(item.first, item.second);
+        }
+        return namedValues;
+    }
+};
+
+/* ---------------------------- UTILITIES FOR PARAMETER REFLECTION ---------------------------- */
+
+/* ======================== UTILITY TEMPLATES FOR PARAMETER REFLECTION ======================== */
+
+#if 1
+template<typename... Params>
+class C2_HIDE _C2Tuple { };
+
+C2_HIDE
+void addC2Params(std::list<const C2FieldDescriptor> &, _C2Tuple<> *) {
+}
+
+template<typename T, typename... Params>
+C2_HIDE
+void addC2Params(std::list<const C2FieldDescriptor> &fields, _C2Tuple<T, Params...> *)
+{
+    //C2Param::index_t index = T::baseIndex;
+    //(void)index;
+    fields.insert(fields.end(), T::fieldList);
+    addC2Params(fields, (_C2Tuple<Params...> *)nullptr);
+}
+
+template<typename... Params>
+C2_HIDE
+std::list<const C2FieldDescriptor> describeC2Params() {
+    std::list<const C2FieldDescriptor> fields;
+    addC2Params(fields, (_C2Tuple<Params...> *)nullptr);
+    return fields;
+}
+
+#endif
+
+/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
+
+}  // namespace android
+
+#endif  // C2UTILS_PARAM_UTILS_H_
+
diff --git a/media/libstagefright/codec2/vndk/include/util/_C2MacroUtils.h b/media/libstagefright/codec2/vndk/include/util/_C2MacroUtils.h
new file mode 100644
index 0000000..04e9ba5
--- /dev/null
+++ b/media/libstagefright/codec2/vndk/include/util/_C2MacroUtils.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef C2UTILS_MACRO_UTILS_H_
+#define C2UTILS_MACRO_UTILS_H_
+
+/** \file
+ * Macro utilities for the utils library used by Codec2 implementations.
+ */
+
+/// \if 0
+
+/* --------------------------------- VARIABLE ARGUMENT COUNTING --------------------------------- */
+
+// remove empty arguments - _C2_ARG() expands to '', while _C2_ARG(x) expands to ', x'
+// _C2_ARGn(...) does the same for n arguments
+#define _C2_ARG(...) , ##__VA_ARGS__
+#define _C2_ARG2(_1, _2) _C2_ARG(_1) _C2_ARG(_2)
+#define _C2_ARG4(_1, _2, _3, _4) _C2_ARG2(_1, _2) _C2_ARG2(_3, _4)
+#define _C2_ARG8(_1, _2, _3, _4, _5, _6, _7, _8) _C2_ARG4(_1, _2, _3, _4) _C2_ARG4(_5, _6, _7, _8)
+#define _C2_ARG16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
+        _C2_ARG8(_1, _2, _3, _4, _5, _6, _7, _8) _C2_ARG8(_9, _10, _11, _12, _13, _14, _15, _16)
+
+// return the 65th argument
+#define _C2_ARGC_3(_, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, \
+        _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, \
+        _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, \
+        _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, ...) _64
+
+/// \endif
+
+/**
+ * Returns the number of arguments.
+ */
+// We do this by prepending 1 and appending 65 designed values such that the 65th element
+// will be the number of arguments.
+#define _C2_ARGC(...) _C2_ARGC_1(0, ##__VA_ARGS__, \
+        64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, \
+        42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \
+        20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+
+/// \if 0
+
+// step 1. remove empty arguments - this is needed to allow trailing comma in enum definitions
+// (NOTE: we don't know which argument will have this trailing comma so we have to try all)
+#define _C2_ARGC_1(_, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, \
+        _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, \
+        _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, \
+        _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, ...) \
+    _C2_ARGC_2(_ _C2_ARG(_0) \
+    _C2_ARG16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
+    _C2_ARG16(_17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32) \
+    _C2_ARG16(_33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48) \
+    _C2_ARG16(_49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64), \
+    ##__VA_ARGS__)
+
+// step 2. this is needed as removed arguments cannot be passed directly as empty into a macro
+#define _C2_ARGC_2(...) _C2_ARGC_3(__VA_ARGS__)
+
+/// \endif
+
+/* -------------------------------- VARIABLE ARGUMENT CONVERSION -------------------------------- */
+
+/// \if 0
+
+// macros that convert _1, _2, _3, ... to fn(_1, arg), fn(_2, arg), fn(_3, arg), ...
+#define _C2_MAP_64(fn, arg, head, ...) fn(head, arg), _C2_MAP_63(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_63(fn, arg, head, ...) fn(head, arg), _C2_MAP_62(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_62(fn, arg, head, ...) fn(head, arg), _C2_MAP_61(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_61(fn, arg, head, ...) fn(head, arg), _C2_MAP_60(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_60(fn, arg, head, ...) fn(head, arg), _C2_MAP_59(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_59(fn, arg, head, ...) fn(head, arg), _C2_MAP_58(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_58(fn, arg, head, ...) fn(head, arg), _C2_MAP_57(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_57(fn, arg, head, ...) fn(head, arg), _C2_MAP_56(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_56(fn, arg, head, ...) fn(head, arg), _C2_MAP_55(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_55(fn, arg, head, ...) fn(head, arg), _C2_MAP_54(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_54(fn, arg, head, ...) fn(head, arg), _C2_MAP_53(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_53(fn, arg, head, ...) fn(head, arg), _C2_MAP_52(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_52(fn, arg, head, ...) fn(head, arg), _C2_MAP_51(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_51(fn, arg, head, ...) fn(head, arg), _C2_MAP_50(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_50(fn, arg, head, ...) fn(head, arg), _C2_MAP_49(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_49(fn, arg, head, ...) fn(head, arg), _C2_MAP_48(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_48(fn, arg, head, ...) fn(head, arg), _C2_MAP_47(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_47(fn, arg, head, ...) fn(head, arg), _C2_MAP_46(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_46(fn, arg, head, ...) fn(head, arg), _C2_MAP_45(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_45(fn, arg, head, ...) fn(head, arg), _C2_MAP_44(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_44(fn, arg, head, ...) fn(head, arg), _C2_MAP_43(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_43(fn, arg, head, ...) fn(head, arg), _C2_MAP_42(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_42(fn, arg, head, ...) fn(head, arg), _C2_MAP_41(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_41(fn, arg, head, ...) fn(head, arg), _C2_MAP_40(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_40(fn, arg, head, ...) fn(head, arg), _C2_MAP_39(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_39(fn, arg, head, ...) fn(head, arg), _C2_MAP_38(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_38(fn, arg, head, ...) fn(head, arg), _C2_MAP_37(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_37(fn, arg, head, ...) fn(head, arg), _C2_MAP_36(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_36(fn, arg, head, ...) fn(head, arg), _C2_MAP_35(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_35(fn, arg, head, ...) fn(head, arg), _C2_MAP_34(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_34(fn, arg, head, ...) fn(head, arg), _C2_MAP_33(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_33(fn, arg, head, ...) fn(head, arg), _C2_MAP_32(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_32(fn, arg, head, ...) fn(head, arg), _C2_MAP_31(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_31(fn, arg, head, ...) fn(head, arg), _C2_MAP_30(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_30(fn, arg, head, ...) fn(head, arg), _C2_MAP_29(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_29(fn, arg, head, ...) fn(head, arg), _C2_MAP_28(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_28(fn, arg, head, ...) fn(head, arg), _C2_MAP_27(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_27(fn, arg, head, ...) fn(head, arg), _C2_MAP_26(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_26(fn, arg, head, ...) fn(head, arg), _C2_MAP_25(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_25(fn, arg, head, ...) fn(head, arg), _C2_MAP_24(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_24(fn, arg, head, ...) fn(head, arg), _C2_MAP_23(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_23(fn, arg, head, ...) fn(head, arg), _C2_MAP_22(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_22(fn, arg, head, ...) fn(head, arg), _C2_MAP_21(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_21(fn, arg, head, ...) fn(head, arg), _C2_MAP_20(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_20(fn, arg, head, ...) fn(head, arg), _C2_MAP_19(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_19(fn, arg, head, ...) fn(head, arg), _C2_MAP_18(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_18(fn, arg, head, ...) fn(head, arg), _C2_MAP_17(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_17(fn, arg, head, ...) fn(head, arg), _C2_MAP_16(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_16(fn, arg, head, ...) fn(head, arg), _C2_MAP_15(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_15(fn, arg, head, ...) fn(head, arg), _C2_MAP_14(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_14(fn, arg, head, ...) fn(head, arg), _C2_MAP_13(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_13(fn, arg, head, ...) fn(head, arg), _C2_MAP_12(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_12(fn, arg, head, ...) fn(head, arg), _C2_MAP_11(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_11(fn, arg, head, ...) fn(head, arg), _C2_MAP_10(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_10(fn, arg, head, ...) fn(head, arg), _C2_MAP_9(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_9(fn, arg, head, ...) fn(head, arg), _C2_MAP_8(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_8(fn, arg, head, ...) fn(head, arg), _C2_MAP_7(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_7(fn, arg, head, ...) fn(head, arg), _C2_MAP_6(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_6(fn, arg, head, ...) fn(head, arg), _C2_MAP_5(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_5(fn, arg, head, ...) fn(head, arg), _C2_MAP_4(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_4(fn, arg, head, ...) fn(head, arg), _C2_MAP_3(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_3(fn, arg, head, ...) fn(head, arg), _C2_MAP_2(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_2(fn, arg, head, ...) fn(head, arg), _C2_MAP_1(fn, arg, ##__VA_ARGS__)
+#define _C2_MAP_1(fn, arg, head, ...) fn(head, arg)
+
+/// \endif
+
+/**
+ * Maps each argument using another macro x -> fn(x, arg)
+ */
+// use wrapper to call the proper mapper based on the number of arguments
+#define _C2_MAP(fn, arg, ...) _C2_MAP__(_C2_ARGC(__VA_ARGS__), fn, arg, ##__VA_ARGS__)
+
+/// \if 0
+
+// evaluate _n so it becomes a number
+#define _C2_MAP__(_n, fn, arg, ...) _C2_MAP_(_n, fn, arg, __VA_ARGS__)
+// call the proper mapper
+#define _C2_MAP_(_n, fn, arg, ...) _C2_MAP_##_n (fn, arg, __VA_ARGS__)
+
+/// \endif
+
+#endif  // C2UTILS_MACRO_UTILS_H_
diff --git a/media/libstagefright/codecs/avc/enc/Android.mk b/media/libstagefright/codecs/avc/enc/Android.mk
index 8249d66..cfca608 100644
--- a/media/libstagefright/codecs/avc/enc/Android.mk
+++ b/media/libstagefright/codecs/avc/enc/Android.mk
@@ -61,12 +61,10 @@
 LOCAL_SHARED_LIBRARIES := \
         libmedia \
         libstagefright_avc_common \
-        libstagefright_enc_common \
         libstagefright_foundation \
         libstagefright_omx \
         libutils \
         liblog \
-        libui
 
 
 LOCAL_MODULE := libstagefright_soft_h264enc
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
index cecc52b..e2bba25 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
@@ -49,58 +49,10 @@
         (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
 
 static const CodecProfileLevel kProfileLevels[] = {
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1  },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2  },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3  },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4  },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel42 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel5  },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel51 },
     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 },
 
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel1  },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel1b },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel11 },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel12 },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel13 },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel2  },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel21 },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel22 },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel3  },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel31 },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel32 },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel4  },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel41 },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel42 },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel5  },
-    { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel51 },
     { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel52 },
 
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel1  },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel1b },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel11 },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel12 },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel13 },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel2  },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel21 },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel22 },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel3  },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel31 },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel32 },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel4  },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel41 },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel42 },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel5  },
-    { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel51 },
     { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel52 },
 };
 
@@ -120,7 +72,8 @@
       mIvColorFormat(IV_YUV_420P),
       mChangingResolution(false),
       mSignalledError(false),
-      mStride(mWidth){
+      mStride(mWidth),
+      mInputOffset(0){
     initPorts(
             1 /* numMinInputBuffers */, kNumBuffers, INPUT_BUF_SIZE,
             1 /* numMinOutputBuffers */, kNumBuffers, CODEC_MIME_TYPE);
@@ -215,6 +168,7 @@
 status_t SoftAVC::resetPlugin() {
     mIsInFlush = false;
     mReceivedEOS = false;
+    mInputOffset = 0;
     memset(mTimeStamps, 0, sizeof(mTimeStamps));
     memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid));
 
@@ -443,8 +397,8 @@
     if (inHeader) {
         ps_dec_ip->u4_ts = timeStampIx;
         ps_dec_ip->pv_stream_buffer =
-            inHeader->pBuffer + inHeader->nOffset;
-        ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen;
+            inHeader->pBuffer + inHeader->nOffset + mInputOffset;
+        ps_dec_ip->u4_num_Bytes = inHeader->nFilledLen - mInputOffset;
     } else {
         ps_dec_ip->u4_ts = 0;
         ps_dec_ip->pv_stream_buffer = NULL;
@@ -515,6 +469,8 @@
 
 void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
     UNUSED(portIndex);
+    OMX_BUFFERHEADERTYPE *inHeader = NULL;
+    BufferInfo *inInfo = NULL;
 
     if (mSignalledError) {
         return;
@@ -541,17 +497,11 @@
     List<BufferInfo *> &outQueue = getPortQueue(kOutputPortIndex);
 
     while (!outQueue.empty()) {
-        BufferInfo *inInfo;
-        OMX_BUFFERHEADERTYPE *inHeader;
-
         BufferInfo *outInfo;
         OMX_BUFFERHEADERTYPE *outHeader;
-        size_t timeStampIx;
+        size_t timeStampIx = 0;
 
-        inInfo = NULL;
-        inHeader = NULL;
-
-        if (!mIsInFlush) {
+        if (!mIsInFlush && (NULL == inHeader)) {
             if (!inQueue.empty()) {
                 inInfo = *inQueue.begin();
                 inHeader = inInfo->mHeader;
@@ -618,7 +568,7 @@
                 return;
             }
             // If input dump is enabled, then write to file
-            DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes);
+            DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
 
             GETTIME(&mTimeStart, NULL);
             /* Compute time elapsed between end of previous decode()
@@ -734,24 +684,26 @@
                     resetPlugin();
                 }
             }
+            mInputOffset += s_dec_op.u4_num_bytes_consumed;
         }
-
-        /* If input EOS is seen and decoder is not in flush mode,
-         * set the decoder in flush mode.
-         * There can be a case where EOS is sent along with last picture data
-         * In that case, only after decoding that input data, decoder has to be
-         * put in flush. This case is handled here  */
-
-        if (mReceivedEOS && !mIsInFlush) {
-            setFlushMode();
-        }
-
-        if (inHeader != NULL) {
+        // If more than 4 bytes are remaining in input, then do not release it
+        if (inHeader != NULL && ((inHeader->nFilledLen - mInputOffset) <= 4)) {
             inInfo->mOwnedByUs = false;
             inQueue.erase(inQueue.begin());
             inInfo = NULL;
             notifyEmptyBufferDone(inHeader);
             inHeader = NULL;
+            mInputOffset = 0;
+
+            /* If input EOS is seen and decoder is not in flush mode,
+             * set the decoder in flush mode.
+             * There can be a case where EOS is sent along with last picture data
+             * In that case, only after decoding that input data, decoder has to be
+             * put in flush. This case is handled here  */
+
+            if (mReceivedEOS && !mIsInFlush) {
+                setFlushMode();
+            }
         }
     }
 }
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.h b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
index b333190..2a71188 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.h
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
@@ -99,6 +99,7 @@
     bool mFlushNeeded;
     bool mSignalledError;
     size_t mStride;
+    size_t mInputOffset;
 
     status_t initDecoder();
     status_t deInitDecoder();
@@ -143,10 +144,10 @@
         ALOGD("Could not open file %s", m_filename);    \
     }                                                   \
 }
-#define DUMP_TO_FILE(m_filename, m_buf, m_size)         \
+#define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)\
 {                                                       \
     FILE *fp = fopen(m_filename, "ab");                 \
-    if (fp != NULL && m_buf != NULL) {                  \
+    if (fp != NULL && m_buf != NULL && m_offset == 0) { \
         int i;                                          \
         i = fwrite(m_buf, 1, m_size, fp);               \
         ALOGD("fwrite ret %d to write %d", i, m_size);  \
@@ -154,10 +155,12 @@
             ALOGD("Error in fwrite, returned %d", i);   \
             perror("Error in write to file");           \
         }                                               \
-        fclose(fp);                                     \
-    } else {                                            \
+    } else if (fp == NULL) {                            \
         ALOGD("Could not write to file %s", m_filename);\
     }                                                   \
+    if (fp) {                                           \
+        fclose(fp);                                     \
+    }                                                   \
 }
 #else /* FILE_DUMP_ENABLE */
 #define INPUT_DUMP_PATH
@@ -166,7 +169,7 @@
 #define OUTPUT_DUMP_EXT
 #define GENERATE_FILE_NAMES()
 #define CREATE_DUMP_FILE(m_filename)
-#define DUMP_TO_FILE(m_filename, m_buf, m_size)
+#define DUMP_TO_FILE(m_filename, m_buf, m_size, m_offset)
 #endif /* FILE_DUMP_ENABLE */
 
 } // namespace android
diff --git a/media/libstagefright/codecs/avcenc/Android.mk b/media/libstagefright/codecs/avcenc/Android.mk
index 2d24584..1b1a1a0 100644
--- a/media/libstagefright/codecs/avcenc/Android.mk
+++ b/media/libstagefright/codecs/avcenc/Android.mk
@@ -17,7 +17,6 @@
 
 LOCAL_SHARED_LIBRARIES  := libmedia
 LOCAL_SHARED_LIBRARIES  += libstagefright_omx
-LOCAL_SHARED_LIBRARIES  += libstagefright_foundation
 LOCAL_SHARED_LIBRARIES  += libutils
 LOCAL_SHARED_LIBRARIES  += liblog
 
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
index b002208..11978a1 100644
--- a/media/libstagefright/codecs/g711/dec/Android.mk
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -9,7 +9,7 @@
         frameworks/native/include/media/openmax
 
 LOCAL_SHARED_LIBRARIES := \
-        libmedia libstagefright_omx libstagefright_foundation libutils liblog
+        libmedia libstagefright_omx libutils liblog
 
 LOCAL_MODULE := libstagefright_soft_g711dec
 LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/gsm/dec/Android.mk b/media/libstagefright/codecs/gsm/dec/Android.mk
index 5d6c289..eed1348 100644
--- a/media/libstagefright/codecs/gsm/dec/Android.mk
+++ b/media/libstagefright/codecs/gsm/dec/Android.mk
@@ -13,7 +13,7 @@
 LOCAL_SANITIZE := signed-integer-overflow unsigned-integer-overflow
 
 LOCAL_SHARED_LIBRARIES := \
-        libmedia libstagefright_omx libstagefright_foundation libutils liblog
+        libmedia libstagefright_omx libutils liblog
 
 LOCAL_STATIC_LIBRARIES := \
         libgsm
diff --git a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
index 5c70387..a3fd336 100644
--- a/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
+++ b/media/libstagefright/codecs/hevcdec/SoftHEVC.cpp
@@ -48,14 +48,6 @@
         (IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
 
 static const CodecProfileLevel kProfileLevels[] = {
-    { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel1  },
-    { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel2  },
-    { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel21 },
-    { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel3  },
-    { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel31 },
-    { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel4  },
-    { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel41 },
-    { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel5  },
     { OMX_VIDEO_HEVCProfileMain, OMX_VIDEO_HEVCMainTierLevel51 },
 };
 
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.mk b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
index 1ff8b47..7b706fe 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
@@ -64,13 +64,9 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libmedia \
-        libstagefright_enc_common \
-        libstagefright_foundation \
         libstagefright_omx \
         libutils \
         liblog \
-        libui
-
 
 LOCAL_MODULE := libstagefright_soft_mpeg4enc
 LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
index 5ed037a..ce28faf 100644
--- a/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
+++ b/media/libstagefright/codecs/mpeg2dec/SoftMPEG2.cpp
@@ -47,14 +47,8 @@
         (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES
 
 static const CodecProfileLevel kProfileLevels[] = {
-    { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelLL  },
-    { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelML  },
-    { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelH14 },
     { OMX_VIDEO_MPEG2ProfileSimple, OMX_VIDEO_MPEG2LevelHL  },
 
-    { OMX_VIDEO_MPEG2ProfileMain  , OMX_VIDEO_MPEG2LevelLL  },
-    { OMX_VIDEO_MPEG2ProfileMain  , OMX_VIDEO_MPEG2LevelML  },
-    { OMX_VIDEO_MPEG2ProfileMain  , OMX_VIDEO_MPEG2LevelH14 },
     { OMX_VIDEO_MPEG2ProfileMain  , OMX_VIDEO_MPEG2LevelHL  },
 };
 
diff --git a/media/libstagefright/codecs/on2/enc/Android.mk b/media/libstagefright/codecs/on2/enc/Android.mk
index f8019d0..5c85bbd 100644
--- a/media/libstagefright/codecs/on2/enc/Android.mk
+++ b/media/libstagefright/codecs/on2/enc/Android.mk
@@ -20,7 +20,6 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libmedia libstagefright_omx libstagefright_foundation libutils liblog \
-        libhardware \
 
 LOCAL_MODULE := libstagefright_soft_vpxenc
 LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/filters/MediaFilter.cpp b/media/libstagefright/filters/MediaFilter.cpp
index f26c840..30e3643 100644
--- a/media/libstagefright/filters/MediaFilter.cpp
+++ b/media/libstagefright/filters/MediaFilter.cpp
@@ -360,25 +360,6 @@
     ALOGV("Sent kWhatEOS.");
 }
 
-void MediaFilter::sendFormatChange() {
-    sp<AMessage> notify = mNotify->dup();
-
-    notify->setInt32("what", kWhatOutputFormatChanged);
-
-    AString mime;
-    CHECK(mOutputFormat->findString("mime", &mime));
-    notify->setString("mime", mime.c_str());
-
-    notify->setInt32("stride", mStride);
-    notify->setInt32("slice-height", mSliceHeight);
-    notify->setInt32("color-format", mColorFormatOut);
-    notify->setRect("crop", 0, 0, mStride - 1, mSliceHeight - 1);
-    notify->setInt32("width", mWidth);
-    notify->setInt32("height", mHeight);
-
-    notify->post();
-}
-
 void MediaFilter::requestFillEmptyInput() {
     if (mPortEOS[kPortIndexInput]) {
         return;
@@ -553,8 +534,6 @@
     notify->post();
     mState = CONFIGURED;
     ALOGV("Handled kWhatConfigureComponent.");
-
-    sendFormatChange();
 }
 
 void MediaFilter::onStart() {
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 8b9472e..e654a01 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -518,9 +518,10 @@
     return err;
 }
 
-status_t LiveSession::seekTo(int64_t timeUs) {
+status_t LiveSession::seekTo(int64_t timeUs, bool precise) {
     sp<AMessage> msg = new AMessage(kWhatSeek, this);
     msg->setInt64("timeUs", timeUs);
+    msg->setInt32("precise", precise);
 
     sp<AMessage> response;
     status_t err = msg->postAndAwaitResponse(&response);
@@ -1441,7 +1442,10 @@
 
 void LiveSession::onSeek(const sp<AMessage> &msg) {
     int64_t timeUs;
+    int32_t precise;
     CHECK(msg->findInt64("timeUs", &timeUs));
+    CHECK(msg->findInt32("precise", &precise));
+    // TODO: add "precise" to changeConfiguration.
     changeConfiguration(timeUs);
 }
 
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 65a824e..4dc529c 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -86,7 +86,7 @@
     status_t disconnect();
 
     // Blocks until seek is complete.
-    status_t seekTo(int64_t timeUs);
+    status_t seekTo(int64_t timeUs, bool precise);
 
     status_t getDuration(int64_t *durationUs) const;
     size_t getTrackCount() const;
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index bda29fa..85ee4ee 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -28,6 +28,7 @@
 class IOMXBufferSource;
 class IOMXObserver;
 struct OMXMaster;
+class OMXBuffer;
 
 struct OMXNodeInstance : public BnOMXNode {
     OMXNodeInstance(
@@ -64,22 +65,6 @@
             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, OMX_U32 allottedSize);
-
-    status_t useGraphicBuffer(
-            OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
-            OMX::buffer_id *buffer);
-
-    status_t updateGraphicBufferInMeta(
-            OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
-            OMX::buffer_id buffer);
-
-    status_t updateNativeHandleInMeta(
-            OMX_U32 portIndex, const sp<NativeHandle> &nativeHandle,
-            OMX::buffer_id buffer);
-
     status_t setInputSurface(
             const sp<IOMXBufferSource> &bufferSource);
 
@@ -87,19 +72,18 @@
             OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
             void **buffer_data, sp<NativeHandle> *native_handle);
 
-    status_t freeBuffer(OMX_U32 portIndex, OMX::buffer_id buffer);
+    status_t useBuffer(
+            OMX_U32 portIndex, const OMXBuffer &omxBuf, buffer_id *buffer);
 
-    status_t fillBuffer(OMX::buffer_id buffer, int fenceFd);
+    status_t freeBuffer(
+            OMX_U32 portIndex, buffer_id buffer);
+
+    status_t fillBuffer(
+            buffer_id buffer, const OMXBuffer &omxBuf, int fenceFd = -1);
 
     status_t emptyBuffer(
-            OMX::buffer_id buffer,
-            OMX_U32 rangeOffset, OMX_U32 rangeLength,
-            OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
-
-    status_t emptyGraphicBuffer(
-            OMX::buffer_id buffer,
-            const sp<GraphicBuffer> &graphicBuffer, OMX_U32 flags,
-            OMX_TICKS timestamp, int fenceFd);
+            buffer_id buffer, const OMXBuffer &omxBuf,
+            OMX_U32 flags, OMX_TICKS timestamp, int fenceFd = -1);
 
     status_t getExtensionIndex(
             const char *parameterName, OMX_INDEXTYPE *index);
@@ -196,9 +180,43 @@
 
     bool isProhibitedIndex_l(OMX_INDEXTYPE index);
 
+    status_t useBuffer(
+            OMX_U32 portIndex, const sp<IMemory> &params,
+            OMX::buffer_id *buffer, OMX_U32 allottedSize);
+
+    status_t useBuffer_l(
+            OMX_U32 portIndex, const sp<IMemory> &params,
+            OMX::buffer_id *buffer, OMX_U32 allottedSize);
+
+    status_t useGraphicBuffer(
+            OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
+            OMX::buffer_id *buffer);
+
+    status_t useGraphicBufferWithMetadata_l(
+            OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
+            OMX::buffer_id *buffer);
+
     status_t useGraphicBuffer2_l(
             OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
             OMX::buffer_id *buffer);
+
+    status_t emptyBuffer(
+            OMX::buffer_id buffer,
+            OMX_U32 rangeOffset, OMX_U32 rangeLength,
+            OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
+
+    status_t emptyGraphicBuffer(
+            OMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
+            OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
+
+    status_t emptyNativeHandleBuffer(
+            OMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
+            OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
+
+    status_t emptyBuffer_l(
+            OMX_BUFFERHEADERTYPE *header,
+            OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr, int fenceFd);
+
     static OMX_ERRORTYPE OnEvent(
             OMX_IN OMX_HANDLETYPE hComponent,
             OMX_IN OMX_PTR pAppData,
@@ -229,10 +247,6 @@
     int retrieveFenceFromMeta_l(
             OMX_BUFFERHEADERTYPE *header, OMX_U32 portIndex);
 
-    status_t emptyBuffer_l(
-            OMX_BUFFERHEADERTYPE *header,
-            OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr, int fenceFd);
-
     // Updates the graphic buffer handle in the metadata buffer for |buffer| and |header| to
     // |graphicBuffer|'s handle. If |updateCodecBuffer| is true, the update will happen in
     // the actual codec buffer (use this if not using emptyBuffer (with no _l) later to
@@ -242,6 +256,10 @@
             OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
             OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
 
+    status_t updateNativeHandleInMeta_l(
+            OMX_U32 portIndex, const sp<NativeHandle> &nativeHandle,
+            OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
+
     sp<IOMXBufferSource> getBufferSource();
     void setBufferSource(const sp<IOMXBufferSource> &bufferSource);
     // Called when omx_message::FILL_BUFFER_DONE is received. (Currently the
diff --git a/media/libstagefright/include/SecureBuffer.h b/media/libstagefright/include/SecureBuffer.h
index 4bb1418..ac7399a 100644
--- a/media/libstagefright/include/SecureBuffer.h
+++ b/media/libstagefright/include/SecureBuffer.h
@@ -34,7 +34,7 @@
  */
 class SecureBuffer : public MediaCodecBuffer {
 public:
-    SecureBuffer(const sp<AMessage> &format, void *ptr, size_t size);
+    SecureBuffer(const sp<AMessage> &format, const void *ptr, size_t size);
     SecureBuffer(const sp<AMessage> &format, const sp<NativeHandle> &handle, size_t size);
 
     virtual ~SecureBuffer() = default;
@@ -42,6 +42,8 @@
     void *getDestinationPointer();
     ICrypto::DestinationType getDestinationType();
 
+    virtual sp<MediaCodecBuffer> clone(const sp<AMessage> &format) override;
+
 private:
     SecureBuffer() = delete;
 
diff --git a/media/libstagefright/include/SharedMemoryBuffer.h b/media/libstagefright/include/SharedMemoryBuffer.h
index 1d7f7a6..c52e5c5 100644
--- a/media/libstagefright/include/SharedMemoryBuffer.h
+++ b/media/libstagefright/include/SharedMemoryBuffer.h
@@ -34,6 +34,8 @@
 
     virtual ~SharedMemoryBuffer() = default;
 
+    virtual sp<MediaCodecBuffer> clone(const sp<AMessage> &format) override;
+
 private:
     SharedMemoryBuffer() = delete;
 
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index b060628..a974671 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -377,6 +377,16 @@
 
     *actualFrameTimeUs = -1ll;
 
+    if (seekTimeUs > INT64_MAX / 1000ll ||
+            seekTimeUs < INT64_MIN / 1000ll ||
+            (mExtractor->mSeekPreRollNs > 0 &&
+                    (seekTimeUs * 1000ll) < INT64_MIN + mExtractor->mSeekPreRollNs) ||
+            (mExtractor->mSeekPreRollNs < 0 &&
+                    (seekTimeUs * 1000ll) > INT64_MAX + mExtractor->mSeekPreRollNs)) {
+        ALOGE("cannot seek to %lld", (long long) seekTimeUs);
+        return;
+    }
+
     const int64_t seekTimeNs = seekTimeUs * 1000ll - mExtractor->mSeekPreRollNs;
 
     mkvparser::Segment* const pSegment = mExtractor->mSegment;
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index ee4fc05..72f8043 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -21,7 +21,6 @@
 
 LOCAL_SHARED_LIBRARIES :=               \
         libbinder                       \
-        libhardware                     \
         libmedia                        \
         libutils                        \
         liblog                          \
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 4575d28..4909100 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -34,6 +34,7 @@
 #include "omx/OMXUtils.h"
 #include <OMX_Component.h>
 #include <OMX_IndexExt.h>
+#include "OMXBuffer.h"
 
 #include <inttypes.h>
 #include "FrameDropper.h"
@@ -524,7 +525,8 @@
         if (mPrevCaptureUs < 0ll) {
             // first capture
             mPrevCaptureUs = timeUs;
-            mPrevFrameUs = timeUs;
+            // adjust the first sample timestamp.
+            mPrevFrameUs = (timeUs * mTimePerFrameUs) / mTimePerCaptureUs;
         } else {
             // snap to nearest capture point
             int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs)
@@ -578,7 +580,7 @@
     const sp<GraphicBuffer> &buffer = codecBuffer.mGraphicBuffer;
     int fenceID = item.mFence->isValid() ? item.mFence->dup() : -1;
 
-    status_t err = mOMXNode->emptyGraphicBuffer(
+    status_t err = mOMXNode->emptyBuffer(
             bufferID, buffer, OMX_BUFFERFLAG_ENDOFFRAME, codecTimeUs, fenceID);
 
     if (err != OK) {
@@ -611,8 +613,8 @@
     CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi));
     IOMX::buffer_id bufferID = codecBuffer.mBufferID;
 
-    status_t err = mOMXNode->emptyGraphicBuffer(
-            bufferID, NULL /* buffer */,
+    status_t err = mOMXNode->emptyBuffer(
+            bufferID, (sp<GraphicBuffer>)NULL,
             OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
             0 /* timestamp */, -1 /* fenceFd */);
     if (err != OK) {
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 41f3e4a..fdc9d7f 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -40,6 +40,7 @@
 #include <media/stagefright/MediaErrors.h>
 #include <utils/misc.h>
 #include <utils/NativeHandle.h>
+#include <media/OMXBuffer.h>
 
 static const OMX_U32 kPortIndexInput = 0;
 static const OMX_U32 kPortIndexOutput = 1;
@@ -901,6 +902,21 @@
 }
 
 status_t OMXNodeInstance::useBuffer(
+        OMX_U32 portIndex,
+        const OMXBuffer &omxBuffer, OMX::buffer_id *buffer) {
+    // TODO: the allotted size is probably no longer needed.
+    if (omxBuffer.mBufferType == OMXBuffer::kBufferTypeSharedMem) {
+        return useBuffer(portIndex, omxBuffer.mMem, buffer, omxBuffer.mAllottedSize);
+    }
+
+    if (omxBuffer.mBufferType == OMXBuffer::kBufferTypeANWBuffer) {
+        return useGraphicBuffer(portIndex, omxBuffer.mGraphicBuffer, buffer);
+    }
+
+    return BAD_VALUE;
+}
+
+status_t OMXNodeInstance::useBuffer(
         OMX_U32 portIndex, const sp<IMemory> &params,
         OMX::buffer_id *buffer, OMX_U32 allottedSize) {
     if (params == NULL || buffer == NULL) {
@@ -913,6 +929,12 @@
         return BAD_VALUE;
     }
 
+    return useBuffer_l(portIndex, params, buffer, allottedSize);
+}
+
+status_t OMXNodeInstance::useBuffer_l(
+        OMX_U32 portIndex, const sp<IMemory> &params,
+        OMX::buffer_id *buffer, OMX_U32 allottedSize) {
     BufferMeta *buffer_meta;
     OMX_BUFFERHEADERTYPE *header;
     OMX_ERRORTYPE err = OMX_ErrorNone;
@@ -937,16 +959,18 @@
 
         if (err != OMX_ErrorNone) {
             CLOG_ERROR(allocateBuffer, err,
-                    SIMPLE_BUFFER(portIndex, (size_t)allottedSize, params->pointer()));
+                    SIMPLE_BUFFER(portIndex, (size_t)allottedSize,
+                            params != NULL ? params->pointer() : NULL));
         }
     } else {
-        OMX_U8 *data = static_cast<OMX_U8 *>(params->pointer());
+        OMX_U8 *data = NULL;
 
         // metadata buffers are not connected cross process
         // use a backup buffer instead of the actual buffer
         if (isMetadata) {
+            // TODO: this logic is very fishy, should it be removed?
             // if we are not connecting the buffers, the sizes must match
-            if (allottedSize != params->size()) {
+            if (params != NULL && allottedSize != params->size()) {
                 CLOG_ERROR(useBuffer, BAD_VALUE, SIMPLE_BUFFER(portIndex, (size_t)allottedSize, data));
                 return BAD_VALUE;
             }
@@ -960,6 +984,10 @@
             buffer_meta = new BufferMeta(
                     params, portIndex, false /* copy */, data);
         } else {
+            // NULL params is allowed only in metadata mode.
+            CHECK(params != NULL);
+            data = static_cast<OMX_U8 *>(params->pointer());
+
             buffer_meta = new BufferMeta(
                     params, portIndex, false /* copy */, NULL);
         }
@@ -1064,6 +1092,13 @@
     }
     Mutex::Autolock autoLock(mLock);
 
+    // First, see if we're in metadata mode. We could be running an experiment to simulate
+    // legacy behavior (preallocated buffers) on devices that supports meta.
+    if (mMetadataType[portIndex] != kMetadataBufferTypeInvalid) {
+        return useGraphicBufferWithMetadata_l(
+                portIndex, graphicBuffer, buffer);
+    }
+
     // See if the newer version of the extension is present.
     OMX_INDEXTYPE index;
     if (OMX_GetExtensionIndex(
@@ -1119,6 +1154,33 @@
     return OK;
 }
 
+status_t OMXNodeInstance::useGraphicBufferWithMetadata_l(
+        OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
+        OMX::buffer_id *buffer) {
+    if (portIndex != kPortIndexOutput) {
+        return BAD_VALUE;
+    }
+
+    OMX_U32 allottedSize = 0;
+    if (mMetadataType[portIndex] == kMetadataBufferTypeGrallocSource) {
+        allottedSize = sizeof(VideoGrallocMetadata);
+    } else if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer) {
+        allottedSize = sizeof(VideoNativeMetadata);
+    } else {
+        return BAD_VALUE;
+    }
+
+    status_t err = useBuffer_l(portIndex, NULL, buffer, allottedSize);
+    if (err != OK) {
+        return err;
+    }
+
+    OMX_BUFFERHEADERTYPE *header = findBufferHeader(*buffer, portIndex);
+
+    return updateGraphicBufferInMeta_l(portIndex, graphicBuffer, *buffer, header);
+
+}
+
 status_t OMXNodeInstance::updateGraphicBufferInMeta_l(
         OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
         OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
@@ -1159,20 +1221,9 @@
     return OK;
 }
 
-status_t OMXNodeInstance::updateGraphicBufferInMeta(
-        OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
-        OMX::buffer_id buffer) {
-    Mutex::Autolock autoLock(mLock);
-    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, portIndex);
-
-    return updateGraphicBufferInMeta_l(
-            portIndex, graphicBuffer, buffer, header);
-}
-
-status_t OMXNodeInstance::updateNativeHandleInMeta(
-        OMX_U32 portIndex, const sp<NativeHandle>& nativeHandle, OMX::buffer_id buffer) {
-    Mutex::Autolock autoLock(mLock);
-    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, portIndex);
+status_t OMXNodeInstance::updateNativeHandleInMeta_l(
+        OMX_U32 portIndex, const sp<NativeHandle>& nativeHandle,
+        OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
     // No need to check |nativeHandle| since NULL is valid for it as below.
     if (header == NULL) {
         ALOGE("b/25884056");
@@ -1340,7 +1391,8 @@
     return StatusFromOMXError(err);
 }
 
-status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer, int fenceFd) {
+status_t OMXNodeInstance::fillBuffer(
+        OMX::buffer_id buffer, const OMXBuffer &omxBuffer, int fenceFd) {
     Mutex::Autolock autoLock(mLock);
 
     OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexOutput);
@@ -1348,6 +1400,20 @@
         ALOGE("b/25884056");
         return BAD_VALUE;
     }
+
+    if (omxBuffer.mBufferType == OMXBuffer::kBufferTypeANWBuffer) {
+        status_t err = updateGraphicBufferInMeta_l(
+                kPortIndexOutput, omxBuffer.mGraphicBuffer, buffer, header);
+
+        if (err != OK) {
+            CLOG_ERROR(fillBuffer, err, FULL_BUFFER(
+                    (intptr_t)header->pBuffer, header, fenceFd));
+            return err;
+        }
+    } else if (omxBuffer.mBufferType != OMXBuffer::kBufferTypePreset) {
+        return BAD_VALUE;
+    }
+
     header->nFilledLen = 0;
     header->nOffset = 0;
     header->nFlags = 0;
@@ -1375,6 +1441,27 @@
 }
 
 status_t OMXNodeInstance::emptyBuffer(
+        buffer_id buffer, const OMXBuffer &omxBuffer,
+        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
+    if (omxBuffer.mBufferType == OMXBuffer::kBufferTypePreset) {
+        return emptyBuffer(
+                buffer, 0, omxBuffer.mRangeLength, flags, timestamp, fenceFd);
+    }
+
+    if (omxBuffer.mBufferType == OMXBuffer::kBufferTypeANWBuffer) {
+        return emptyGraphicBuffer(
+                buffer, omxBuffer.mGraphicBuffer, flags, timestamp, fenceFd);
+    }
+
+    if (omxBuffer.mBufferType == OMXBuffer::kBufferTypeNativeHandle) {
+        return emptyNativeHandleBuffer(
+                buffer, omxBuffer.mNativeHandle, flags, timestamp, fenceFd);
+    }
+
+    return BAD_VALUE;
+}
+
+status_t OMXNodeInstance::emptyBuffer(
         OMX::buffer_id buffer,
         OMX_U32 rangeOffset, OMX_U32 rangeLength,
         OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
@@ -1601,6 +1688,31 @@
     return timestamp;
 }
 
+status_t OMXNodeInstance::emptyNativeHandleBuffer(
+        OMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
+        OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
+    Mutex::Autolock autoLock(mLock);
+
+    OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
+    if (header == NULL) {
+        ALOGE("b/25884056");
+        return BAD_VALUE;
+    }
+
+    status_t err = updateNativeHandleInMeta_l(
+            kPortIndexInput, nativeHandle, buffer, header);
+    if (err != OK) {
+        CLOG_ERROR(emptyNativeHandleBuffer, err, FULL_BUFFER(
+                (intptr_t)header->pBuffer, header, fenceFd));
+        return err;
+    }
+
+    header->nOffset = 0;
+    header->nFilledLen = (nativeHandle == NULL) ? 0 : sizeof(VideoNativeMetadata);
+
+    return emptyBuffer_l(header, flags, timestamp, (intptr_t)header->pBuffer, fenceFd);
+}
+
 void OMXNodeInstance::codecBufferFilled(omx_message &msg) {
     Mutex::Autolock autoLock(mBufferIDLock);
 
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index ea99e53..935d7bf 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -38,6 +38,7 @@
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/SimpleDecodingSource.h>
+#include <media/OMXBuffer.h>
 
 #define DEFAULT_TIMEOUT         500000
 
@@ -210,8 +211,7 @@
         buffer.mFlags = 0;
         CHECK(buffer.mMemory != NULL);
 
-        err = mOMXNode->useBuffer(
-                portIndex, buffer.mMemory, &buffer.mID, buffer.mMemory->size());
+        err = mOMXNode->useBuffer(portIndex, buffer.mMemory, &buffer.mID);
         EXPECT_SUCCESS(err, "useBuffer");
 
         buffers->push(buffer);
@@ -338,7 +338,7 @@
            "executing state.");
 
     for (size_t i = 0; i < outputBuffers.size(); ++i) {
-        err = mOMXNode->fillBuffer(outputBuffers[i].mID);
+        err = mOMXNode->fillBuffer(outputBuffers[i].mID, OMXBuffer::sPreset);
         EXPECT_SUCCESS(err, "fillBuffer");
 
         outputBuffers.editItemAt(i).mFlags |= kBufferBusy;
@@ -363,7 +363,7 @@
     }
 
     for (size_t i = 0; i < outputBuffers.size(); ++i) {
-        err = mOMXNode->fillBuffer(outputBuffers[i].mID);
+        err = mOMXNode->fillBuffer(outputBuffers[i].mID, OMXBuffer::sPreset);
         EXPECT_SUCCESS(err, "fillBuffer");
 
         outputBuffers.editItemAt(i).mFlags |= kBufferBusy;
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index 902f66c..c5322a4 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -24,7 +24,6 @@
         libbinder                       \
         libcutils                       \
         liblog                          \
-        libgui                          \
         libmedia                        \
         libstagefright                  \
         libstagefright_foundation       \
diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk
deleted file mode 100644
index 08d202b..0000000
--- a/media/libstagefright/yuv/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=               \
-        YUVImage.cpp            \
-        YUVCanvas.cpp
-
-LOCAL_SHARED_LIBRARIES :=       \
-        libcutils \
-        liblog
-
-LOCAL_MODULE:= libstagefright_yuv
-
-
-LOCAL_CFLAGS += -Werror -Wall
-LOCAL_SANITIZE := signed-integer-overflow
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/yuv/MODULE_LICENSE_APACHE2 b/media/libstagefright/yuv/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/media/libstagefright/yuv/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/media/libstagefright/yuv/NOTICE b/media/libstagefright/yuv/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/media/libstagefright/yuv/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2005-2008, 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.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/media/libstagefright/yuv/YUVCanvas.cpp b/media/libstagefright/yuv/YUVCanvas.cpp
deleted file mode 100644
index 4c9fee8..0000000
--- a/media/libstagefright/yuv/YUVCanvas.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_NDEBUG 0
-#define LOG_TAG "YUVCanvas"
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/YUVCanvas.h>
-#include <media/stagefright/YUVImage.h>
-#include <ui/Rect.h>
-
-namespace android {
-
-YUVCanvas::YUVCanvas(YUVImage &yuvImage)
-    : mYUVImage(yuvImage) {
-}
-
-YUVCanvas::~YUVCanvas() {
-}
-
-void YUVCanvas::FillYUV(uint8_t yValue, uint8_t uValue, uint8_t vValue) {
-    for (int32_t y = 0; y < mYUVImage.height(); ++y) {
-        for (int32_t x = 0; x < mYUVImage.width(); ++x) {
-            mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
-        }
-    }
-}
-
-void YUVCanvas::FillYUVRectangle(const Rect& rect,
-        uint8_t yValue, uint8_t uValue, uint8_t vValue) {
-    for (int32_t y = rect.top; y < rect.bottom; ++y) {
-        for (int32_t x = rect.left; x < rect.right; ++x) {
-            mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
-        }
-    }
-}
-
-void YUVCanvas::CopyImageRect(
-        const Rect& srcRect,
-        int32_t destStartX, int32_t destStartY,
-        const YUVImage &srcImage) {
-
-    // Try fast copy first
-    if (YUVImage::fastCopyRectangle(
-                srcRect,
-                destStartX, destStartY,
-                srcImage, mYUVImage)) {
-        return;
-    }
-
-    int32_t srcStartX = srcRect.left;
-    int32_t srcStartY = srcRect.top;
-    for (int32_t offsetY = 0; offsetY < srcRect.height(); ++offsetY) {
-        for (int32_t offsetX = 0; offsetX < srcRect.width(); ++offsetX) {
-            int32_t srcX = srcStartX + offsetX;
-            int32_t srcY = srcStartY + offsetY;
-
-            int32_t destX = destStartX + offsetX;
-            int32_t destY = destStartY + offsetY;
-
-            uint8_t yValue;
-            uint8_t uValue;
-            uint8_t vValue;
-
-            srcImage.getPixelValue(srcX, srcY, &yValue, &uValue, &vValue);
-            mYUVImage.setPixelValue(destX, destY, yValue, uValue, vValue);
-        }
-    }
-}
-
-void YUVCanvas::downsample(
-        int32_t srcOffsetX, int32_t srcOffsetY,
-        int32_t skipX, int32_t skipY,
-        const YUVImage &srcImage) {
-    // TODO: Add a low pass filter for downsampling.
-
-    // Check that srcImage is big enough to fill mYUVImage.
-    CHECK((srcOffsetX + (mYUVImage.width() - 1) * skipX) < srcImage.width());
-    CHECK((srcOffsetY + (mYUVImage.height() - 1) * skipY) < srcImage.height());
-
-    uint8_t yValue;
-    uint8_t uValue;
-    uint8_t vValue;
-
-    int32_t srcY = srcOffsetY;
-    for (int32_t y = 0; y < mYUVImage.height(); ++y) {
-        int32_t srcX = srcOffsetX;
-        for (int32_t x = 0; x < mYUVImage.width(); ++x) {
-            srcImage.getPixelValue(srcX, srcY, &yValue, &uValue, &vValue);
-            mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
-
-            srcX += skipX;
-        }
-        srcY += skipY;
-    }
-}
-
-}  // namespace android
diff --git a/media/libstagefright/yuv/YUVImage.cpp b/media/libstagefright/yuv/YUVImage.cpp
deleted file mode 100644
index c098135..0000000
--- a/media/libstagefright/yuv/YUVImage.cpp
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_NDEBUG 0
-#define LOG_TAG "YUVImage"
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/YUVImage.h>
-#include <ui/Rect.h>
-
-namespace android {
-
-YUVImage::YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height) {
-    mYUVFormat = yuvFormat;
-    mWidth = width;
-    mHeight = height;
-
-    size_t numberOfBytes = bufferSize(yuvFormat, width, height);
-    uint8_t *buffer = new uint8_t[numberOfBytes];
-    mBuffer = buffer;
-    mOwnBuffer = true;
-
-    initializeYUVPointers();
-}
-
-YUVImage::YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height, uint8_t *buffer) {
-    mYUVFormat = yuvFormat;
-    mWidth = width;
-    mHeight = height;
-    mBuffer = buffer;
-    mOwnBuffer = false;
-
-    initializeYUVPointers();
-}
-
-//static
-size_t YUVImage::bufferSize(YUVFormat yuvFormat, int32_t width, int32_t height) {
-    int32_t numberOfPixels = width*height;
-    size_t numberOfBytes = 0;
-    if (yuvFormat == YUV420Planar || yuvFormat == YUV420SemiPlanar) {
-        // Y takes numberOfPixels bytes and U/V take numberOfPixels/4 bytes each.
-        numberOfBytes = (size_t)(numberOfPixels + (numberOfPixels >> 1));
-    } else {
-        ALOGE("Format not supported");
-    }
-    return numberOfBytes;
-}
-
-bool YUVImage::initializeYUVPointers() {
-    int32_t numberOfPixels = mWidth * mHeight;
-
-    if (mYUVFormat == YUV420Planar) {
-        mYdata = (uint8_t *)mBuffer;
-        mUdata = mYdata + numberOfPixels;
-        mVdata = mUdata + (numberOfPixels >> 2);
-    } else if (mYUVFormat == YUV420SemiPlanar) {
-        // U and V channels are interleaved as VUVUVU.
-        // So V data starts at the end of Y channel and
-        // U data starts right after V's start.
-        mYdata = (uint8_t *)mBuffer;
-        mVdata = mYdata + numberOfPixels;
-        mUdata = mVdata + 1;
-    } else {
-        ALOGE("Format not supported");
-        return false;
-    }
-    return true;
-}
-
-YUVImage::~YUVImage() {
-    if (mOwnBuffer) delete[] mBuffer;
-}
-
-bool YUVImage::getOffsets(int32_t x, int32_t y,
-        int32_t *yOffset, int32_t *uOffset, int32_t *vOffset) const {
-    *yOffset = y*mWidth + x;
-
-    int32_t uvOffset = (y >> 1) * (mWidth >> 1) + (x >> 1);
-    if (mYUVFormat == YUV420Planar) {
-        *uOffset = uvOffset;
-        *vOffset = uvOffset;
-    } else if (mYUVFormat == YUV420SemiPlanar) {
-        // Since U and V channels are interleaved, offsets need
-        // to be doubled.
-        *uOffset = 2*uvOffset;
-        *vOffset = 2*uvOffset;
-    } else {
-        ALOGE("Format not supported");
-        return false;
-    }
-
-    return true;
-}
-
-bool YUVImage::getOffsetIncrementsPerDataRow(
-        int32_t *yDataOffsetIncrement,
-        int32_t *uDataOffsetIncrement,
-        int32_t *vDataOffsetIncrement) const {
-    *yDataOffsetIncrement = mWidth;
-
-    int32_t uvDataOffsetIncrement = mWidth >> 1;
-
-    if (mYUVFormat == YUV420Planar) {
-        *uDataOffsetIncrement = uvDataOffsetIncrement;
-        *vDataOffsetIncrement = uvDataOffsetIncrement;
-    } else if (mYUVFormat == YUV420SemiPlanar) {
-        // Since U and V channels are interleaved, offsets need
-        // to be doubled.
-        *uDataOffsetIncrement = 2*uvDataOffsetIncrement;
-        *vDataOffsetIncrement = 2*uvDataOffsetIncrement;
-    } else {
-        ALOGE("Format not supported");
-        return false;
-    }
-
-    return true;
-}
-
-uint8_t* YUVImage::getYAddress(int32_t offset) const {
-    return mYdata + offset;
-}
-
-uint8_t* YUVImage::getUAddress(int32_t offset) const {
-    return mUdata + offset;
-}
-
-uint8_t* YUVImage::getVAddress(int32_t offset) const {
-    return mVdata + offset;
-}
-
-bool YUVImage::getYUVAddresses(int32_t x, int32_t y,
-        uint8_t **yAddr, uint8_t **uAddr, uint8_t **vAddr) const {
-    int32_t yOffset;
-    int32_t uOffset;
-    int32_t vOffset;
-    if (!getOffsets(x, y, &yOffset, &uOffset, &vOffset)) return false;
-
-    *yAddr = getYAddress(yOffset);
-    *uAddr = getUAddress(uOffset);
-    *vAddr = getVAddress(vOffset);
-
-    return true;
-}
-
-bool YUVImage::validPixel(int32_t x, int32_t y) const {
-    return (x >= 0 && x < mWidth &&
-            y >= 0 && y < mHeight);
-}
-
-bool YUVImage::getPixelValue(int32_t x, int32_t y,
-        uint8_t *yPtr, uint8_t *uPtr, uint8_t *vPtr) const {
-    CHECK(validPixel(x, y));
-
-    uint8_t *yAddr;
-    uint8_t *uAddr;
-    uint8_t *vAddr;
-    if (!getYUVAddresses(x, y, &yAddr, &uAddr, &vAddr)) return false;
-
-    *yPtr = *yAddr;
-    *uPtr = *uAddr;
-    *vPtr = *vAddr;
-
-    return true;
-}
-
-bool YUVImage::setPixelValue(int32_t x, int32_t y,
-        uint8_t yValue, uint8_t uValue, uint8_t vValue) {
-    CHECK(validPixel(x, y));
-
-    uint8_t *yAddr;
-    uint8_t *uAddr;
-    uint8_t *vAddr;
-    if (!getYUVAddresses(x, y, &yAddr, &uAddr, &vAddr)) return false;
-
-    *yAddr = yValue;
-    *uAddr = uValue;
-    *vAddr = vValue;
-
-    return true;
-}
-
-void YUVImage::fastCopyRectangle420Planar(
-        const Rect& srcRect,
-        int32_t destStartX, int32_t destStartY,
-        const YUVImage &srcImage, YUVImage &destImage) {
-    CHECK(srcImage.mYUVFormat == YUV420Planar);
-    CHECK(destImage.mYUVFormat == YUV420Planar);
-
-    int32_t srcStartX = srcRect.left;
-    int32_t srcStartY = srcRect.top;
-    int32_t width = srcRect.width();
-    int32_t height = srcRect.height();
-
-    // Get source and destination start addresses
-    uint8_t *ySrcAddrBase;
-    uint8_t *uSrcAddrBase;
-    uint8_t *vSrcAddrBase;
-    srcImage.getYUVAddresses(srcStartX, srcStartY,
-            &ySrcAddrBase, &uSrcAddrBase, &vSrcAddrBase);
-
-    uint8_t *yDestAddrBase;
-    uint8_t *uDestAddrBase;
-    uint8_t *vDestAddrBase;
-    destImage.getYUVAddresses(destStartX, destStartY,
-            &yDestAddrBase, &uDestAddrBase, &vDestAddrBase);
-
-    // Get source and destination offset increments incurred in going
-    // from one data row to next.
-    int32_t ySrcOffsetIncrement;
-    int32_t uSrcOffsetIncrement;
-    int32_t vSrcOffsetIncrement;
-    srcImage.getOffsetIncrementsPerDataRow(
-            &ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);
-
-    int32_t yDestOffsetIncrement;
-    int32_t uDestOffsetIncrement = 0;
-    int32_t vDestOffsetIncrement = 0;
-    destImage.getOffsetIncrementsPerDataRow(
-            &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);
-
-    // Copy Y
-    {
-        size_t numberOfYBytesPerRow = (size_t) width;
-        uint8_t *ySrcAddr = ySrcAddrBase;
-        uint8_t *yDestAddr = yDestAddrBase;
-        for (int32_t offsetY = 0; offsetY < height; ++offsetY) {
-            memcpy(yDestAddr, ySrcAddr, numberOfYBytesPerRow);
-
-            ySrcAddr += ySrcOffsetIncrement;
-            yDestAddr += yDestOffsetIncrement;
-        }
-    }
-
-    // Copy U
-    {
-        size_t numberOfUBytesPerRow = (size_t) (width >> 1);
-        uint8_t *uSrcAddr = uSrcAddrBase;
-        uint8_t *uDestAddr = uDestAddrBase;
-        // Every other row has an entry for U/V channel values. Hence only
-        // go half the height.
-        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
-            memcpy(uDestAddr, uSrcAddr, numberOfUBytesPerRow);
-
-            uSrcAddr += uSrcOffsetIncrement;
-            uDestAddr += uDestOffsetIncrement;
-        }
-    }
-
-    // Copy V
-    {
-        size_t numberOfVBytesPerRow = (size_t) (width >> 1);
-        uint8_t *vSrcAddr = vSrcAddrBase;
-        uint8_t *vDestAddr = vDestAddrBase;
-        // Every other pixel row has a U/V data row. Hence only go half the height.
-        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
-            memcpy(vDestAddr, vSrcAddr, numberOfVBytesPerRow);
-
-            vSrcAddr += vSrcOffsetIncrement;
-            vDestAddr += vDestOffsetIncrement;
-        }
-    }
-}
-
-void YUVImage::fastCopyRectangle420SemiPlanar(
-        const Rect& srcRect,
-        int32_t destStartX, int32_t destStartY,
-        const YUVImage &srcImage, YUVImage &destImage) {
-    CHECK(srcImage.mYUVFormat == YUV420SemiPlanar);
-    CHECK(destImage.mYUVFormat == YUV420SemiPlanar);
-
-    int32_t srcStartX = srcRect.left;
-    int32_t srcStartY = srcRect.top;
-    int32_t width = srcRect.width();
-    int32_t height = srcRect.height();
-
-    // Get source and destination start addresses
-    uint8_t *ySrcAddrBase;
-    uint8_t *uSrcAddrBase;
-    uint8_t *vSrcAddrBase;
-    srcImage.getYUVAddresses(srcStartX, srcStartY,
-            &ySrcAddrBase, &uSrcAddrBase, &vSrcAddrBase);
-
-    uint8_t *yDestAddrBase;
-    uint8_t *uDestAddrBase;
-    uint8_t *vDestAddrBase;
-    destImage.getYUVAddresses(destStartX, destStartY,
-            &yDestAddrBase, &uDestAddrBase, &vDestAddrBase);
-
-    // Get source and destination offset increments incurred in going
-    // from one data row to next.
-    int32_t ySrcOffsetIncrement;
-    int32_t uSrcOffsetIncrement;
-    int32_t vSrcOffsetIncrement;
-    srcImage.getOffsetIncrementsPerDataRow(
-            &ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);
-
-    int32_t yDestOffsetIncrement;
-    int32_t uDestOffsetIncrement;
-    int32_t vDestOffsetIncrement = 0;
-    destImage.getOffsetIncrementsPerDataRow(
-            &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);
-
-    // Copy Y
-    {
-        size_t numberOfYBytesPerRow = (size_t) width;
-        uint8_t *ySrcAddr = ySrcAddrBase;
-        uint8_t *yDestAddr = yDestAddrBase;
-        for (int32_t offsetY = 0; offsetY < height; ++offsetY) {
-            memcpy(yDestAddr, ySrcAddr, numberOfYBytesPerRow);
-
-            ySrcAddr = ySrcAddr + ySrcOffsetIncrement;
-            yDestAddr = yDestAddr + yDestOffsetIncrement;
-        }
-    }
-
-    // Copy UV
-    {
-        // UV are interleaved. So number of UV bytes per row is 2*(width/2).
-        size_t numberOfUVBytesPerRow = (size_t) width;
-        uint8_t *vSrcAddr = vSrcAddrBase;
-        uint8_t *vDestAddr = vDestAddrBase;
-        // Every other pixel row has a U/V data row. Hence only go half the height.
-        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
-            memcpy(vDestAddr, vSrcAddr, numberOfUVBytesPerRow);
-
-            vSrcAddr += vSrcOffsetIncrement;
-            vDestAddr += vDestOffsetIncrement;
-        }
-    }
-}
-
-// static
-bool YUVImage::fastCopyRectangle(
-        const Rect& srcRect,
-        int32_t destStartX, int32_t destStartY,
-        const YUVImage &srcImage, YUVImage &destImage) {
-    if (srcImage.mYUVFormat == destImage.mYUVFormat) {
-        if (srcImage.mYUVFormat == YUV420Planar) {
-            fastCopyRectangle420Planar(
-                    srcRect,
-                    destStartX, destStartY,
-                    srcImage, destImage);
-        } else if (srcImage.mYUVFormat == YUV420SemiPlanar) {
-            fastCopyRectangle420SemiPlanar(
-                    srcRect,
-                    destStartX, destStartY,
-                    srcImage, destImage);
-        }
-        return true;
-    }
-    return false;
-}
-
-uint8_t clamp(uint8_t v, uint8_t minValue, uint8_t maxValue) {
-    CHECK(maxValue >= minValue);
-
-    if (v < minValue) return minValue;
-    else if (v > maxValue) return maxValue;
-    else return v;
-}
-
-void YUVImage::yuv2rgb(uint8_t yValue, uint8_t uValue, uint8_t vValue,
-        uint8_t *r, uint8_t *g, uint8_t *b) const {
-    int rTmp = yValue + (1.370705 * (vValue-128));
-    int gTmp = yValue - (0.698001 * (vValue-128)) - (0.337633 * (uValue-128));
-    int bTmp = yValue + (1.732446 * (uValue-128));
-
-    *r = clamp(rTmp, 0, 255);
-    *g = clamp(gTmp, 0, 255);
-    *b = clamp(bTmp, 0, 255);
-}
-
-bool YUVImage::writeToPPM(const char *filename) const {
-    FILE *fp = fopen(filename, "w");
-    if (fp == NULL) {
-        return false;
-    }
-    fprintf(fp, "P3\n");
-    fprintf(fp, "%d %d\n", mWidth, mHeight);
-    fprintf(fp, "255\n");
-    for (int32_t y = 0; y < mHeight; ++y) {
-        for (int32_t x = 0; x < mWidth; ++x) {
-            uint8_t yValue = 0u;
-            uint8_t uValue = 0u;
-            uint8_t vValue = 0u;
-            getPixelValue(x, y, &yValue, &uValue, & vValue);
-
-            uint8_t rValue;
-            uint8_t gValue;
-            uint8_t bValue;
-            yuv2rgb(yValue, uValue, vValue, &rValue, &gValue, &bValue);
-
-            fprintf(fp, "%d %d %d\n", (int32_t)rValue, (int32_t)gValue, (int32_t)bValue);
-        }
-    }
-    fclose(fp);
-    return true;
-}
-
-}  // namespace android
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index f3f3b1a..ffbfcbb 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -15,13 +15,9 @@
 	main_mediaserver.cpp
 
 LOCAL_SHARED_LIBRARIES := \
-	libcamera_metadata \
-	libcamera_client \
-	libcameraservice \
 	libresourcemanagerservice \
 	liblog \
 	libcutils \
-	libmedia \
 	libmediaplayerservice \
 	libutils \
 	libbinder \
@@ -33,7 +29,6 @@
 
 LOCAL_C_INCLUDES := \
     frameworks/av/media/libmediaplayerservice \
-    frameworks/av/services/camera/libcameraservice \
     frameworks/av/services/mediaresourcemanager \
 
 LOCAL_MODULE:= mediaserver
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 0e9bc34..0356753 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -519,7 +519,7 @@
 
 // Wait for result of readDataAsync
 int MtpDataPacket::readDataWait(struct usb_device *device) {
-    struct usb_request *req = usb_request_wait(device);
+    struct usb_request *req = usb_request_wait(device, -1);
     return (req ? req->actual_length : -1);
 }
 
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index e3d2ec6..82a2627 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -39,6 +39,12 @@
 
 namespace android {
 
+namespace {
+
+static constexpr int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
+
+}  // namespace
+
 #if 0
 static bool isMtpDevice(uint16_t vendor, uint16_t product) {
     // Sandisk Sansa Fuze
@@ -84,15 +90,18 @@
                 interface->bInterfaceSubClass == 1 && // Still Image Capture
                 interface->bInterfaceProtocol == 1)     // Picture Transfer Protocol (PIMA 15470)
             {
-                char* manufacturerName = usb_device_get_manufacturer_name(device);
-                char* productName = usb_device_get_product_name(device);
+                char* manufacturerName = usb_device_get_manufacturer_name(device,
+                        USB_CONTROL_TRANSFER_TIMEOUT_MS);
+                char* productName = usb_device_get_product_name(device,
+                        USB_CONTROL_TRANSFER_TIMEOUT_MS);
                 ALOGD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName);
                 free(manufacturerName);
                 free(productName);
             } else if (interface->bInterfaceClass == 0xFF &&
                     interface->bInterfaceSubClass == 0xFF &&
                     interface->bInterfaceProtocol == 0) {
-                char* interfaceName = usb_device_get_string(device, interface->iInterface);
+                char* interfaceName = usb_device_get_string(device, interface->iInterface,
+                        USB_CONTROL_TRANSFER_TIMEOUT_MS);
                 if (!interfaceName) {
                     continue;
                 } else if (strcmp(interfaceName, "MTP")) {
@@ -102,8 +111,10 @@
                 free(interfaceName);
 
                 // Looks like an android style MTP device
-                char* manufacturerName = usb_device_get_manufacturer_name(device);
-                char* productName = usb_device_get_product_name(device);
+                char* manufacturerName = usb_device_get_manufacturer_name(device,
+                        USB_CONTROL_TRANSFER_TIMEOUT_MS);
+                char* productName = usb_device_get_product_name(device,
+                        USB_CONTROL_TRANSFER_TIMEOUT_MS);
                 ALOGD("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
                 free(manufacturerName);
                 free(productName);
diff --git a/media/mtp/MtpEventPacket.cpp b/media/mtp/MtpEventPacket.cpp
index 8e13ea9..d9ef311 100644
--- a/media/mtp/MtpEventPacket.cpp
+++ b/media/mtp/MtpEventPacket.cpp
@@ -66,7 +66,7 @@
 }
 
 int MtpEventPacket::readResponse(struct usb_device *device) {
-    struct usb_request* const req = usb_request_wait(device);
+    struct usb_request* const req = usb_request_wait(device, -1);
     if (req) {
         mPacketSize = req->actual_length;
         return req->actual_length;
diff --git a/media/ndk/Android.mk b/media/ndk/Android.mk
index 74729e4..a4f999f 100644
--- a/media/ndk/Android.mk
+++ b/media/ndk/Android.mk
@@ -45,7 +45,6 @@
 LOCAL_SHARED_LIBRARIES := \
     libbinder \
     libmedia \
-    libmediadrm \
     libstagefright \
     libstagefright_foundation \
     liblog \
diff --git a/media/utils/BatteryNotifier.cpp b/media/utils/BatteryNotifier.cpp
index 341d391..7a7321f 100644
--- a/media/utils/BatteryNotifier.cpp
+++ b/media/utils/BatteryNotifier.cpp
@@ -29,7 +29,7 @@
     BatteryNotifier::getInstance().onBatteryStatServiceDied();
 }
 
-BatteryNotifier::BatteryNotifier() : mVideoRefCount(0), mAudioRefCount(0) {}
+BatteryNotifier::BatteryNotifier() {}
 
 BatteryNotifier::~BatteryNotifier() {
     Mutex::Autolock _l(mLock);
@@ -38,67 +38,73 @@
     }
 }
 
-void BatteryNotifier::noteStartVideo() {
+void BatteryNotifier::noteStartVideo(int uid) {
     Mutex::Autolock _l(mLock);
     sp<IBatteryStats> batteryService = getBatteryService_l();
-    if (mVideoRefCount == 0 && batteryService != nullptr) {
-        batteryService->noteStartVideo(AID_MEDIA);
+    if (mVideoRefCounts[uid] == 0 && batteryService != nullptr) {
+        batteryService->noteStartVideo(uid);
     }
-    mVideoRefCount++;
+    mVideoRefCounts[uid]++;
 }
 
-void BatteryNotifier::noteStopVideo() {
+void BatteryNotifier::noteStopVideo(int uid) {
     Mutex::Autolock _l(mLock);
-    if (mVideoRefCount == 0) {
-        ALOGW("%s: video refcount is broken.", __FUNCTION__);
+    if (mVideoRefCounts.find(uid) == mVideoRefCounts.end()) {
+        ALOGW("%s: video refcount is broken for uid(%d).", __FUNCTION__, (int)uid);
         return;
     }
 
     sp<IBatteryStats> batteryService = getBatteryService_l();
 
-    mVideoRefCount--;
-    if (mVideoRefCount == 0 && batteryService != nullptr) {
-        batteryService->noteStopVideo(AID_MEDIA);
+    mVideoRefCounts[uid]--;
+    if (mVideoRefCounts[uid] == 0) {
+        if (batteryService != nullptr) {
+            batteryService->noteStopVideo(uid);
+        }
+        mVideoRefCounts.erase(uid);
     }
 }
 
 void BatteryNotifier::noteResetVideo() {
     Mutex::Autolock _l(mLock);
     sp<IBatteryStats> batteryService = getBatteryService_l();
-    mVideoRefCount = 0;
+    mVideoRefCounts.clear();
     if (batteryService != nullptr) {
         batteryService->noteResetVideo();
     }
 }
 
-void BatteryNotifier::noteStartAudio() {
+void BatteryNotifier::noteStartAudio(int uid) {
     Mutex::Autolock _l(mLock);
     sp<IBatteryStats> batteryService = getBatteryService_l();
-    if (mAudioRefCount == 0 && batteryService != nullptr) {
-        batteryService->noteStartAudio(AID_AUDIOSERVER);
+    if (mAudioRefCounts[uid] == 0 && batteryService != nullptr) {
+        batteryService->noteStartAudio(uid);
     }
-    mAudioRefCount++;
+    mAudioRefCounts[uid]++;
 }
 
-void BatteryNotifier::noteStopAudio() {
+void BatteryNotifier::noteStopAudio(int uid) {
     Mutex::Autolock _l(mLock);
-    if (mAudioRefCount == 0) {
-        ALOGW("%s: audio refcount is broken.", __FUNCTION__);
+    if (mAudioRefCounts.find(uid) == mAudioRefCounts.end()) {
+        ALOGW("%s: audio refcount is broken for uid(%d).", __FUNCTION__, (int)uid);
         return;
     }
 
     sp<IBatteryStats> batteryService = getBatteryService_l();
 
-    mAudioRefCount--;
-    if (mAudioRefCount == 0 && batteryService != nullptr) {
-        batteryService->noteStopAudio(AID_AUDIOSERVER);
+    mAudioRefCounts[uid]--;
+    if (mAudioRefCounts[uid] == 0) {
+        if (batteryService != nullptr) {
+            batteryService->noteStopAudio(uid);
+        }
+        mAudioRefCounts.erase(uid);
     }
 }
 
 void BatteryNotifier::noteResetAudio() {
     Mutex::Autolock _l(mLock);
     sp<IBatteryStats> batteryService = getBatteryService_l();
-    mAudioRefCount = 0;
+    mAudioRefCounts.clear();
     if (batteryService != nullptr) {
         batteryService->noteResetAudio();
     }
@@ -176,7 +182,7 @@
     Mutex::Autolock _l(mLock);
     mBatteryStatService.clear();
     mDeathNotifier.clear();
-    // Do not reset mVideoRefCount and mAudioRefCount here. The ref
+    // Do not reset mVideoRefCounts and mAudioRefCounts here. The ref
     // counting is independent of the battery service availability.
     // We need this if battery service becomes available after media
     // started.
@@ -205,11 +211,13 @@
         // Notify start now if mediaserver or audioserver is already started.
         // 1) mediaserver and audioserver is started before batterystats service
         // 2) batterystats server may have crashed.
-        if (mVideoRefCount > 0) {
-            mBatteryStatService->noteStartVideo(AID_MEDIA);
+        std::map<int, int>::iterator it = mVideoRefCounts.begin();
+        for (; it != mVideoRefCounts.end(); ++it) {
+            mBatteryStatService->noteStartVideo(it->first);
         }
-        if (mAudioRefCount > 0) {
-            mBatteryStatService->noteStartAudio(AID_AUDIOSERVER);
+        it = mAudioRefCounts.begin();
+        for (; it != mAudioRefCounts.end(); ++it) {
+            mBatteryStatService->noteStartAudio(it->first);
         }
         // TODO: Notify for camera and flashlight state as well?
     }
diff --git a/media/utils/include/mediautils/BatteryNotifier.h b/media/utils/include/mediautils/BatteryNotifier.h
index 49048042..2ba4c76 100644
--- a/media/utils/include/mediautils/BatteryNotifier.h
+++ b/media/utils/include/mediautils/BatteryNotifier.h
@@ -37,11 +37,11 @@
 public:
     ~BatteryNotifier();
 
-    void noteStartVideo();
-    void noteStopVideo();
+    void noteStartVideo(int uid);
+    void noteStopVideo(int uid);
     void noteResetVideo();
-    void noteStartAudio();
-    void noteStopAudio();
+    void noteStartAudio(int uid);
+    void noteStopAudio(int uid);
     void noteResetAudio();
     void noteFlashlightOn(const String8& id, int uid);
     void noteFlashlightOff(const String8& id, int uid);
@@ -58,8 +58,8 @@
     };
 
     Mutex mLock;
-    int mVideoRefCount;
-    int mAudioRefCount;
+    std::map<int, int> mVideoRefCounts;
+    std::map<int, int> mAudioRefCounts;
     std::map<std::pair<String8, int>, bool> mFlashlightState;
     std::map<std::pair<String8, int>, bool> mCameraState;
     sp<IBatteryStats> mBatteryStatService;
diff --git a/radio/Android.mk b/radio/Android.mk
index 0377328..be5d283 100644
--- a/radio/Android.mk
+++ b/radio/Android.mk
@@ -27,7 +27,6 @@
 	libutils \
 	liblog \
 	libbinder \
-	libhardware \
 	libradio_metadata
 
 #LOCAL_C_INCLUDES += \
diff --git a/radio/IRadio.cpp b/radio/IRadio.cpp
index 0881a91..ebf3859 100644
--- a/radio/IRadio.cpp
+++ b/radio/IRadio.cpp
@@ -112,7 +112,7 @@
         if (status == NO_ERROR) {
             status = (status_t)reply.readInt32();
             if (status == NO_ERROR) {
-                int muteread = reply.readInt32();
+                int32_t muteread = reply.readInt32();
                 *mute = muteread != 0;
             }
         }
@@ -145,12 +145,12 @@
         return status;
     }
 
-    virtual status_t tune(unsigned int channel, unsigned int subChannel)
+    virtual status_t tune(uint32_t channel, uint32_t subChannel)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
-        data.writeInt32(channel);
-        data.writeInt32(subChannel);
+        data.writeUint32(channel);
+        data.writeUint32(subChannel);
         status_t status = remote()->transact(TUNE, data, &reply);
         if (status == NO_ERROR) {
             status = (status_t)reply.readInt32();
@@ -177,27 +177,29 @@
         }
         radio_metadata_t *metadata = info->metadata;
         data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
+        if (metadata != NULL) {
+            data.writeUint32(1);
+        } else {
+            data.writeUint32(0);
+        }
         status_t status = remote()->transact(GET_PROGRAM_INFORMATION, data, &reply);
         if (status == NO_ERROR) {
             status = (status_t)reply.readInt32();
             if (status == NO_ERROR) {
                 reply.read(info, sizeof(struct radio_program_info));
+                // restore local metadata pointer
                 info->metadata = metadata;
-                if (metadata == NULL) {
-                    return status;
+
+                uint32_t metatataSize = reply.readUint32();
+                if ((metadata != NULL) && (metatataSize != 0)) {
+                    radio_metadata_t *newMetadata = (radio_metadata_t *)malloc(metatataSize);
+                    if (newMetadata == NULL) {
+                        return NO_MEMORY;
+                    }
+                    reply.read(newMetadata, metatataSize);
+                    status = radio_metadata_add_metadata(&info->metadata, newMetadata);
+                    free(newMetadata);
                 }
-                size_t size = (size_t)reply.readInt32();
-                if (size == 0) {
-                    return status;
-                }
-                metadata =
-                    (radio_metadata_t *)calloc(size / sizeof(unsigned int), sizeof(unsigned int));
-                if (metadata == NULL) {
-                    return NO_MEMORY;
-                }
-                reply.read(metadata, size);
-                status = radio_metadata_add_metadata(&info->metadata, metadata);
-                free(metadata);
             }
         }
         return status;
@@ -288,8 +290,8 @@
         }
         case TUNE: {
             CHECK_INTERFACE(IRadio, data, reply);
-            unsigned int channel = (unsigned int)data.readInt32();
-            unsigned int subChannel = (unsigned int)data.readInt32();
+            uint32_t channel = data.readUint32();
+            uint32_t subChannel = data.readUint32();
             status_t status = tune(channel, subChannel);
             reply->writeInt32(status);
             return NO_ERROR;
@@ -303,22 +305,27 @@
         case GET_PROGRAM_INFORMATION: {
             CHECK_INTERFACE(IRadio, data, reply);
             struct radio_program_info info;
-
-            status_t status = radio_metadata_allocate(&info.metadata, 0, 0);
-            if (status != NO_ERROR) {
-                return status;
+            status_t status;
+            // query metadata only if requested by remote side
+            if (data.readUint32() == 1) {
+                status = radio_metadata_allocate(&info.metadata, 0, 0);
+                if (status != NO_ERROR) {
+                    return status;
+                }
+            } else {
+                info.metadata = NULL;
             }
             status = getProgramInformation(&info);
+
             reply->writeInt32(status);
             if (status == NO_ERROR) {
                 reply->write(&info, sizeof(struct radio_program_info));
-                int count = radio_metadata_get_count(info.metadata);
-                if (count > 0) {
+                if ((info.metadata != NULL) && (radio_metadata_get_count(info.metadata) > 0)) {
                     size_t size = radio_metadata_get_size(info.metadata);
-                    reply->writeInt32(size);
+                    reply->writeUint32((uint32_t)size);
                     reply->write(info.metadata, size);
                 } else {
-                    reply->writeInt32(0);
+                    reply->writeUint32(0);
                 }
             }
             radio_metadata_deallocate(info.metadata);
diff --git a/radio/IRadioService.cpp b/radio/IRadioService.cpp
index be7d21e..72e3a61 100644
--- a/radio/IRadioService.cpp
+++ b/radio/IRadioService.cpp
@@ -16,8 +16,7 @@
 */
 
 #define LOG_TAG "BpRadioService"
-//
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 
 #include <utils/Log.h>
 #include <utils/Errors.h>
@@ -58,12 +57,12 @@
         }
         Parcel data, reply;
         data.writeInterfaceToken(IRadioService::getInterfaceDescriptor());
-        unsigned int numModulesReq = (properties == NULL) ? 0 : *numModules;
+        uint32_t numModulesReq = (properties == NULL) ? 0 : *numModules;
         data.writeInt32(numModulesReq);
         status_t status = remote()->transact(LIST_MODULES, data, &reply);
         if (status == NO_ERROR) {
             status = (status_t)reply.readInt32();
-            *numModules = (unsigned int)reply.readInt32();
+            *numModules = (uint32_t)reply.readInt32();
         }
         ALOGV("listModules() status %d got *numModules %d", status, *numModules);
         if (status == NO_ERROR) {
@@ -120,11 +119,11 @@
     switch(code) {
         case LIST_MODULES: {
             CHECK_INTERFACE(IRadioService, data, reply);
-            unsigned int numModulesReq = data.readInt32();
+            uint32_t numModulesReq = data.readInt32();
             if (numModulesReq > MAX_ITEMS_PER_LIST) {
                 numModulesReq = MAX_ITEMS_PER_LIST;
             }
-            unsigned int numModules = numModulesReq;
+            uint32_t numModules = numModulesReq;
             struct radio_properties *properties =
                     (struct radio_properties *)calloc(numModulesReq,
                                                       sizeof(struct radio_properties));
diff --git a/radio/Radio.cpp b/radio/Radio.cpp
index 3c04fb0..fa39589 100644
--- a/radio/Radio.cpp
+++ b/radio/Radio.cpp
@@ -240,20 +240,31 @@
         return;
     }
 
+    // The event layout in shared memory is:
+    // sizeof(struct radio_event) bytes : the event itself
+    // 4 bytes                          : metadata size or 0
+    // N bytes                          : metadata if present
     struct radio_event *event = (struct radio_event *)eventMemory->pointer();
+    uint32_t metadataOffset = sizeof(struct radio_event) + sizeof(uint32_t);
+    uint32_t metadataSize = *(uint32_t *)((uint8_t *)event + metadataOffset - sizeof(uint32_t));
+
     // restore local metadata pointer from offset
     switch (event->type) {
     case RADIO_EVENT_TUNED:
     case RADIO_EVENT_AF_SWITCH:
-        if (event->info.metadata != NULL) {
+        if (metadataSize != 0) {
             event->info.metadata =
-                    (radio_metadata_t *)((char *)event + (size_t)event->info.metadata);
+                    (radio_metadata_t *)((uint8_t *)event + metadataOffset);
+        } else {
+            event->info.metadata = 0;
         }
         break;
     case RADIO_EVENT_METADATA:
-        if (event->metadata != NULL) {
+        if (metadataSize != 0) {
             event->metadata =
-                    (radio_metadata_t *)((char *)event + (size_t)event->metadata);
+                    (radio_metadata_t *)((uint8_t *)event + metadataOffset);
+        } else {
+            event->metadata = 0;
         }
         break;
     default:
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index f35373f..1657c08 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -31,7 +31,8 @@
     AudioMixer.cpp.arm          \
     BufferProviders.cpp         \
     PatchPanel.cpp              \
-    StateQueue.cpp
+    StateQueue.cpp              \
+    BufLog.cpp
 
 LOCAL_C_INCLUDES := \
     $(TOPDIR)frameworks/av/services/audiopolicy \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 04fb8ba..7e10e48 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -67,6 +67,9 @@
 #include <mediautils/BatteryNotifier.h>
 #include <private/android_filesystem_config.h>
 
+//#define BUFLOG_NDEBUG 0
+#include <BufLog.h>
+
 // ----------------------------------------------------------------------------
 
 // Note: the following macro is used for extremely verbose logging message.  In
@@ -441,6 +444,8 @@
         }
 #endif
 
+        BUFLOG_RESET;
+
         if (locked) {
             mLock.unlock();
         }
diff --git a/services/audioflinger/BufLog.cpp b/services/audioflinger/BufLog.cpp
new file mode 100644
index 0000000..9680eb5
--- /dev/null
+++ b/services/audioflinger/BufLog.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "BufLog.h"
+#define LOG_TAG "BufLog"
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include "log/log.h"
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+// ------------------------------
+// BufLogSingleton
+// ------------------------------
+pthread_once_t onceControl = PTHREAD_ONCE_INIT;
+
+BufLog *BufLogSingleton::mInstance = NULL;
+
+void BufLogSingleton::initOnce() {
+    mInstance = new BufLog();
+    ALOGW("=====================================\n" \
+            "Warning: BUFLOG is defined in some part of your code.\n" \
+            "This will create large audio dumps in %s.\n" \
+            "=====================================\n", BUFLOG_BASE_PATH);
+}
+
+BufLog *BufLogSingleton::instance() {
+    pthread_once(&onceControl, initOnce);
+    return mInstance;
+}
+
+bool BufLogSingleton::instanceExists() {
+    return mInstance != NULL;
+}
+
+// ------------------------------
+// BufLog
+// ------------------------------
+
+BufLog::BufLog() {
+    memset(mStreams, 0, sizeof(mStreams));
+}
+
+BufLog::~BufLog() {
+    android::Mutex::Autolock autoLock(mLock);
+
+    for (unsigned int id = 0; id < BUFLOG_MAXSTREAMS; id++) {
+        BufLogStream *pBLStream = mStreams[id];
+        if (pBLStream != NULL) {
+            delete pBLStream ;
+            mStreams[id] = NULL;
+        }
+    }
+}
+
+size_t BufLog::write(int streamid, const char *tag, int format, int channels,
+        int samplingRate, size_t maxBytes, const void *buf, size_t size) {
+    unsigned int id = streamid % BUFLOG_MAXSTREAMS;
+    android::Mutex::Autolock autoLock(mLock);
+
+    BufLogStream *pBLStream = mStreams[id];
+
+    if (pBLStream == NULL) {
+        pBLStream = mStreams[id] = new BufLogStream(id, tag, format, channels,
+                samplingRate, maxBytes);
+        ALOG_ASSERT(pBLStream != NULL, "BufLogStream Failed to be created");
+    }
+
+    return pBLStream->write(buf, size);
+}
+
+void BufLog::reset() {
+    android::Mutex::Autolock autoLock(mLock);
+    ALOGV("Resetting all BufLogs");
+    int count = 0;
+
+    for (unsigned int id = 0; id < BUFLOG_MAXSTREAMS; id++) {
+        BufLogStream *pBLStream = mStreams[id];
+        if (pBLStream != NULL) {
+            delete pBLStream;
+            mStreams[id] = NULL;
+            count++;
+        }
+    }
+    ALOGV("Reset %d BufLogs", count);
+}
+
+// ------------------------------
+// BufLogStream
+// ------------------------------
+
+BufLogStream::BufLogStream(unsigned int id,
+        const char *tag,
+        unsigned int format,
+        unsigned int channels,
+        unsigned int samplingRate,
+        size_t maxBytes = 0) : mId(id), mFormat(format), mChannels(channels),
+                mSamplingRate(samplingRate), mMaxBytes(maxBytes) {
+    mByteCount = 0l;
+    mPaused = false;
+    if (tag != NULL) {
+        strncpy(mTag, tag, BUFLOGSTREAM_MAX_TAGSIZE);
+    } else {
+        mTag[0] = 0;
+    }
+    ALOGV("Creating BufLogStream id:%d tag:%s format:%d ch:%d sr:%d maxbytes:%zu", mId, mTag,
+            mFormat, mChannels, mSamplingRate, mMaxBytes);
+
+    //open file (s), info about tag, format, etc.
+    //timestamp
+    char timeStr[16];   //size 16: format %Y%m%d%H%M%S 14 chars + string null terminator
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    struct tm tm;
+    localtime_r(&tv.tv_sec, &tm);
+    strftime(timeStr, sizeof(timeStr), "%Y%m%d%H%M%S", &tm);
+    char logPath[BUFLOG_MAX_PATH_SIZE];
+    snprintf(logPath, BUFLOG_MAX_PATH_SIZE, "%s/%s_%d_%s_%d_%d_%d.raw", BUFLOG_BASE_PATH, timeStr,
+            mId, mTag, mFormat, mChannels, mSamplingRate);
+    ALOGV("data output: %s", logPath);
+
+    mFile = fopen(logPath, "wb");
+    if (mFile != NULL) {
+        ALOGV("Success creating file at: %p", mFile);
+    } else {
+        ALOGE("Error: could not create file BufLogStream %s", strerror(errno));
+    }
+}
+
+void BufLogStream::closeStream_l() {
+    ALOGV("Closing BufLogStream id:%d tag:%s", mId, mTag);
+    if (mFile != NULL) {
+        fclose(mFile);
+        mFile = NULL;
+    }
+}
+
+BufLogStream::~BufLogStream() {
+    ALOGV("Destroying BufLogStream id:%d tag:%s", mId, mTag);
+    android::Mutex::Autolock autoLock(mLock);
+    closeStream_l();
+}
+
+size_t BufLogStream::write(const void *buf, size_t size) {
+
+    size_t bytes = 0;
+    if (!mPaused && mFile != NULL) {
+        if (size > 0 && buf != NULL) {
+            android::Mutex::Autolock autoLock(mLock);
+            if (mMaxBytes > 0) {
+                size = MIN(size, mMaxBytes - mByteCount);
+            }
+            bytes = fwrite(buf, 1, size, mFile);
+            mByteCount += bytes;
+            if (mMaxBytes > 0 && mMaxBytes == mByteCount) {
+                closeStream_l();
+            }
+        }
+        ALOGV("wrote %zu/%zu bytes to BufLogStream %d tag:%s. Total Bytes: %zu", bytes, size, mId,
+                mTag, mByteCount);
+    } else {
+        ALOGV("Warning: trying to write to %s BufLogStream id:%d tag:%s",
+                mPaused ? "paused" : "closed", mId, mTag);
+    }
+    return bytes;
+}
+
+bool BufLogStream::setPause(bool pause) {
+    bool old = mPaused;
+    mPaused = pause;
+    return old;
+}
+
+void BufLogStream::finalize() {
+    android::Mutex::Autolock autoLock(mLock);
+    closeStream_l();
+}
diff --git a/services/audioflinger/BufLog.h b/services/audioflinger/BufLog.h
new file mode 100644
index 0000000..1b402f4
--- /dev/null
+++ b/services/audioflinger/BufLog.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_BUFLOG_H
+#define ANDROID_AUDIO_BUFLOG_H
+
+/*
+ * BUFLOG creates up to BUFLOG_MAXSTREAMS simultaneous streams [0:15] of audio buffer data
+ * and saves them to disk. The files are stored in the path specified in BUFLOG_BASE_PATH and
+ * are named following this format:
+ *   YYYYMMDDHHMMSS_id_format_channels_samplingrate.raw
+ *
+ * Normally we strip BUFLOG dumps from release builds.
+ * You can modify this (for example with "#define BUFLOG_NDEBUG 0"
+ * at the top of your source file) to change that behavior.
+ *
+ * usage:
+ * - Add this to the top of the source file you want to debug:
+ *   #define BUFLOG_NDEBUG 0
+ *   #include "BufLog.h"
+ *
+ * - dump an audio buffer
+ *  BUFLOG(buff_id, buff_tag, format, channels, sampling_rate, max_bytes, buff_pointer, buff_size);
+ *
+ *  buff_id:   int [0:15]   buffer id. If a buffer doesn't exist, it is created the first time.
+ *  buff_tag:  char*        string tag used on stream filename and logs
+ *  format:    int          Audio format (audio_format_t see audio.h)
+ *  channels:  int          Channel Count
+ *  sampling_rate:  int     Sampling rate in Hz. e.g. 8000, 16000, 44100, 48000, etc
+ *  max_bytes: int [0 or positive number]
+ *                          Maximum size of the file (in bytes) to be output.
+ *                          If the value is 0, no limit.
+ *  buff_pointer: void *    Pointer to audio buffer.
+ *  buff_size:  int         Size (in bytes) of the current audio buffer to be stored.
+ *
+ *
+ *  Example usage:
+ *    int format       = mConfig.outputCfg.format;
+ *    int channels     = audio_channel_count_from_out_mask(mConfig.outputCfg.channels);
+ *    int samplingRate = mConfig.outputCfg.samplingRate;
+ *    int frameCount   = mConfig.outputCfg.buffer.frameCount;
+ *    int frameSize    = audio_bytes_per_sample((audio_format_t)format) * channels;
+ *    int buffSize     = frameCount * frameSize;
+ *    long maxBytes = 10 * samplingRate * frameSize; //10 seconds max
+ *  BUFLOG(11, "loudnes_enhancer_out", format, channels, samplingRate, maxBytes,
+ *                               mConfig.outputCfg.buffer.raw, buffSize);
+ *
+ *  Other macros:
+ *  BUFLOG_EXISTS       returns true if there is an instance of BufLog
+ *
+ *  BUFLOG_RESET        If an instance of BufLog exists, it stops the capture and closes all
+ *                      streams.
+ *                      If a new call to BUFLOG(..) is done, new streams are created.
+ */
+
+#ifndef BUFLOG_NDEBUG
+#ifdef NDEBUG
+#define BUFLOG_NDEBUG 1
+#else
+#define BUFLOG_NDEBUG 0
+#endif
+#endif
+
+/*
+ * Simplified macro to send a buffer.
+ */
+#ifndef BUFLOG
+#define __BUFLOG(STREAMID, TAG, FORMAT, CHANNELS, SAMPLINGRATE, MAXBYTES, BUF, SIZE) \
+    BufLogSingleton::instance()->write(STREAMID, TAG, FORMAT, CHANNELS, SAMPLINGRATE, MAXBYTES, \
+            BUF, SIZE)
+#if BUFLOG_NDEBUG
+#define BUFLOG(STREAMID, TAG, FORMAT, CHANNELS, SAMPLINGRATE, MAXBYTES, BUF, SIZE) \
+    do { if (0) {  } } while (0)
+#else
+#define BUFLOG(STREAMID, TAG, FORMAT, CHANNELS, SAMPLINGRATE, MAXBYTES, BUF, SIZE) \
+    __BUFLOG(STREAMID, TAG, FORMAT, CHANNELS, SAMPLINGRATE, MAXBYTES, BUF, SIZE)
+#endif
+#endif
+
+#ifndef BUFLOG_EXISTS
+#define BUFLOG_EXISTS BufLogSingleton::instanceExists()
+#endif
+
+#ifndef BUFLOG_RESET
+#define BUFLOG_RESET do { if (BufLogSingleton::instanceExists()) { \
+    BufLogSingleton::instance()->reset(); } } while (0)
+#endif
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <utils/Mutex.h>
+
+//BufLog configuration
+#define BUFLOGSTREAM_MAX_TAGSIZE    32
+#define BUFLOG_BASE_PATH            "/data/misc/audioserver"
+#define BUFLOG_MAX_PATH_SIZE        300
+
+class BufLogStream {
+public:
+    BufLogStream(unsigned int id,
+            const char *tag,
+            unsigned int format,
+            unsigned int channels,
+            unsigned int samplingRate,
+            size_t maxBytes);
+    ~BufLogStream();
+
+    // write buffer to stream
+    //  buf:  pointer to buffer
+    //  size: number of bytes to write
+    size_t          write(const void *buf, size_t size);
+
+    // pause/resume stream
+    //  pause: true = paused, false = not paused
+    //  return value: previous state of stream (paused or not).
+    bool            setPause(bool pause);
+
+    // will stop the stream and close any open file
+    // the stream can't be reopen. Instead, a new stream (and file) should be created.
+    void            finalize();
+
+private:
+    bool                mPaused;
+    const unsigned int  mId;
+    char                mTag[BUFLOGSTREAM_MAX_TAGSIZE + 1];
+    const unsigned int  mFormat;
+    const unsigned int  mChannels;
+    const unsigned int  mSamplingRate;
+    const size_t        mMaxBytes;
+    size_t              mByteCount;
+    FILE                *mFile;
+    mutable android::Mutex mLock;
+
+    void            closeStream_l();
+};
+
+
+class BufLog {
+public:
+    BufLog();
+    ~BufLog();
+    BufLog(BufLog const&) {};
+
+    //  streamid:      int [0:BUFLOG_MAXSTREAMS-1]   buffer id.
+    //                  If a buffer doesn't exist, it is created the first time is referenced
+    //  tag:           char*  string tag used on stream filename and logs
+    //  format:        int Audio format (audio_format_t see audio.h)
+    //  channels:      int          Channel Count
+    //  samplingRate:  int Sampling rate in Hz. e.g. 8000, 16000, 44100, 48000, etc
+    //  maxBytes:      int [0 or positive number]
+    //                  Maximum size of the file (in bytes) to be output.
+    //                  If the value is 0, no limit.
+    //  size:          int Size (in bytes) of the current audio buffer to be written.
+    //  buf:           void *    Pointer to audio buffer.
+    size_t          write(int streamid,
+                        const char *tag,
+                        int format,
+                        int channels,
+                        int samplingRate,
+                        size_t maxBytes,
+                        const void *buf,
+                        size_t size);
+
+    // reset will stop and close all active streams, thus finalizing any open file.
+    //  New streams will be created if write() is called again.
+    void            reset();
+
+protected:
+    static const unsigned int BUFLOG_MAXSTREAMS = 16;
+    BufLogStream    *mStreams[BUFLOG_MAXSTREAMS];
+    mutable android::Mutex mLock;
+};
+
+class BufLogSingleton {
+public:
+    static BufLog   *instance();
+    static bool     instanceExists();
+
+private:
+    static void     initOnce();
+    static BufLog   *mInstance;
+};
+
+#endif //ANDROID_AUDIO_BUFLOG_H
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 6b23e56..4ae5a02 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -33,6 +33,7 @@
 #include <utils/Trace.h>
 
 #include <private/media/AudioTrackShared.h>
+#include <private/android_filesystem_config.h>
 #include <audio_utils/conversion.h>
 #include <audio_utils/primitives.h>
 #include <audio_utils/format.h>
@@ -1040,7 +1041,8 @@
     }
 
     if (!mNotifiedBatteryStart) {
-        BatteryNotifier::getInstance().noteStartAudio();
+        // TODO: call this function for each track when it becomes active.
+        BatteryNotifier::getInstance().noteStartAudio(AID_AUDIOSERVER);
         mNotifiedBatteryStart = true;
     }
     gBoottime.acquire(mWakeLockToken);
@@ -1067,7 +1069,8 @@
     }
 
     if (mNotifiedBatteryStart) {
-        BatteryNotifier::getInstance().noteStopAudio();
+        // TODO: call this function for each track when it becomes inactive.
+        BatteryNotifier::getInstance().noteStopAudio(AID_AUDIOSERVER);
         mNotifiedBatteryStart = false;
     }
 }
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index d598e7e..91cc3d2 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -31,7 +31,6 @@
     liblog \
     libbinder \
     libmedia \
-    libhardware \
     libhardware_legacy \
     libserviceutility
 
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 4b32691..c60b49a 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -234,9 +234,9 @@
 
     virtual status_t startAudioSource(const struct audio_port_config *source,
                                       const audio_attributes_t *attributes,
-                                      audio_io_handle_t *handle,
+                                      audio_patch_handle_t *handle,
                                       uid_t uid) = 0;
-    virtual status_t stopAudioSource(audio_io_handle_t handle) = 0;
+    virtual status_t stopAudioSource(audio_patch_handle_t handle) = 0;
 
     virtual status_t setMasterMono(bool mono) = 0;
     virtual status_t getMasterMono(bool *mono) = 0;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h
index 4ab7cf0..7e1e24d 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioSourceDescriptor.h
@@ -50,7 +50,7 @@
 };
 
 class AudioSourceCollection :
-        public DefaultKeyedVector< audio_io_handle_t, sp<AudioSourceDescriptor> >
+        public DefaultKeyedVector< audio_patch_handle_t, sp<AudioSourceDescriptor> >
 {
 public:
     status_t dump(int fd) const;
diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk
index b247e21..e6de8ae 100644
--- a/services/audiopolicy/enginedefault/Android.mk
+++ b/services/audiopolicy/enginedefault/Android.mk
@@ -43,6 +43,5 @@
     liblog \
     libcutils \
     libutils \
-    libaudioutils \
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 7c955e5..2f01b02 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3026,7 +3026,7 @@
 
 status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *source,
                                   const audio_attributes_t *attributes,
-                                  audio_io_handle_t *handle,
+                                  audio_patch_handle_t *handle,
                                   uid_t uid)
 {
     ALOGV("%s source %p attributes %p handle %p", __FUNCTION__, source, attributes, handle);
@@ -3034,7 +3034,7 @@
         return BAD_VALUE;
     }
 
-    *handle = AUDIO_IO_HANDLE_NONE;
+    *handle = AUDIO_PATCH_HANDLE_NONE;
 
     if (source->role != AUDIO_PORT_ROLE_SOURCE ||
             source->type != AUDIO_PORT_TYPE_DEVICE) {
@@ -3141,7 +3141,7 @@
     return NO_ERROR;
 }
 
-status_t AudioPolicyManager::stopAudioSource(audio_io_handle_t handle __unused)
+status_t AudioPolicyManager::stopAudioSource(audio_patch_handle_t handle __unused)
 {
     sp<AudioSourceDescriptor> sourceDesc = mAudioSources.valueFor(handle);
     ALOGV("%s handle %d", __FUNCTION__, handle);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 46f0625..52fa082 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -228,9 +228,9 @@
 
         virtual status_t startAudioSource(const struct audio_port_config *source,
                                           const audio_attributes_t *attributes,
-                                          audio_io_handle_t *handle,
+                                          audio_patch_handle_t *handle,
                                           uid_t uid);
-        virtual status_t stopAudioSource(audio_io_handle_t handle);
+        virtual status_t stopAudioSource(audio_patch_handle_t handle);
 
         virtual status_t setMasterMono(bool mono);
         virtual status_t getMasterMono(bool *mono);
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index f6da920..53c54bb 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -712,7 +712,7 @@
 
 status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
                                   const audio_attributes_t *attributes,
-                                  audio_io_handle_t *handle)
+                                  audio_patch_handle_t *handle)
 {
     Mutex::Autolock _l(mLock);
     if (mAudioPolicyManager == NULL) {
@@ -723,7 +723,7 @@
                                                  IPCThreadState::self()->getCallingUid());
 }
 
-status_t AudioPolicyService::stopAudioSource(audio_io_handle_t handle)
+status_t AudioPolicyService::stopAudioSource(audio_patch_handle_t handle)
 {
     Mutex::Autolock _l(mLock);
     if (mAudioPolicyManager == NULL) {
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
index 517fba1..a5b96fe 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp
@@ -610,12 +610,12 @@
 
 status_t AudioPolicyService::startAudioSource(const struct audio_port_config *source,
                                   const audio_attributes_t *attributes,
-                                  audio_io_handle_t *handle)
+                                  audio_patch_handle_t *handle)
 {
     return INVALID_OPERATION;
 }
 
-status_t AudioPolicyService::stopAudioSource(audio_io_handle_t handle)
+status_t AudioPolicyService::stopAudioSource(audio_patch_handle_t handle)
 {
     return INVALID_OPERATION;
 }
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index f34473c..a310735 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -200,8 +200,8 @@
 
     virtual status_t startAudioSource(const struct audio_port_config *source,
                                       const audio_attributes_t *attributes,
-                                      audio_io_handle_t *handle);
-    virtual status_t stopAudioSource(audio_io_handle_t handle);
+                                      audio_patch_handle_t *handle);
+    virtual status_t stopAudioSource(audio_patch_handle_t handle);
 
     virtual status_t setMasterMono(bool mono);
     virtual status_t getMasterMono(bool *mono);
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 89a1bb6..7feed6b 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -65,12 +65,11 @@
     libcamera_client \
     libgui \
     libhardware \
-    libsync \
     libcamera_metadata \
     libjpeg \
     libmemunreachable
 
-LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder libcamera_client
 
 LOCAL_C_INCLUDES += \
     system/media/private/camera/include \
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 9d5f33c..9a7839b 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -238,6 +238,10 @@
     {
         String8 supportedPreviewFpsRange;
         for (size_t i=0; i < availableFpsRanges.count; i += 2) {
+            if (!isFpsSupported(availablePreviewSizes,
+                HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, availableFpsRanges.data.i32[i+1])) {
+                continue;
+            }
             if (i != 0) supportedPreviewFpsRange += ",";
             supportedPreviewFpsRange += String8::format("(%d,%d)",
                     availableFpsRanges.data.i32[i] * kFpsToApiScale,
@@ -255,7 +259,10 @@
             // from the [min, max] fps range use the max value
             int fps = fpsFromRange(availableFpsRanges.data.i32[i],
                                    availableFpsRanges.data.i32[i+1]);
-
+            if (!isFpsSupported(availablePreviewSizes,
+                    HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, fps)) {
+                continue;
+            }
             // de-dupe frame rates
             if (sortedPreviewFrameRates.indexOf(fps) == NAME_NOT_FOUND) {
                 sortedPreviewFrameRates.add(fps);
@@ -951,21 +958,40 @@
         return NO_INIT;
     }
 
+    // Get supported preview fps ranges.
+    Vector<Size> supportedPreviewSizes;
+    Vector<FpsRange> supportedPreviewFpsRanges;
+    const Size PREVIEW_SIZE_BOUND = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT };
+    status_t res = getFilteredSizes(PREVIEW_SIZE_BOUND, &supportedPreviewSizes);
+    if (res != OK) return res;
+    for (size_t i=0; i < availableFpsRanges.count; i += 2) {
+        if (!isFpsSupported(supportedPreviewSizes,
+                HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, availableFpsRanges.data.i32[i+1])) {
+            continue;
+        }
+        FpsRange fpsRange = {availableFpsRanges.data.i32[i], availableFpsRanges.data.i32[i+1]};
+        supportedPreviewFpsRanges.add(fpsRange);
+    }
+    if (supportedPreviewFpsRanges.size() == 0) {
+        ALOGE("Supported preview fps range is empty");
+        return NO_INIT;
+    }
+
     int32_t bestStillCaptureFpsRange[2] = {
-        availableFpsRanges.data.i32[0], availableFpsRanges.data.i32[1]
+        supportedPreviewFpsRanges[0].low, supportedPreviewFpsRanges[0].high
     };
     int32_t curRange =
             bestStillCaptureFpsRange[1] - bestStillCaptureFpsRange[0];
-    for (size_t i = 2; i < availableFpsRanges.count; i += 2) {
+    for (size_t i = 1; i < supportedPreviewFpsRanges.size(); i ++) {
         int32_t nextRange =
-                availableFpsRanges.data.i32[i + 1] -
-                availableFpsRanges.data.i32[i];
+                supportedPreviewFpsRanges[i].high -
+                supportedPreviewFpsRanges[i].low;
         if ( (nextRange > curRange) ||       // Maximize size of FPS range first
                 (nextRange == curRange &&    // Then minimize low-end FPS
-                 bestStillCaptureFpsRange[0] > availableFpsRanges.data.i32[i])) {
+                 bestStillCaptureFpsRange[0] > supportedPreviewFpsRanges[i].low)) {
 
-            bestStillCaptureFpsRange[0] = availableFpsRanges.data.i32[i];
-            bestStillCaptureFpsRange[1] = availableFpsRanges.data.i32[i + 1];
+            bestStillCaptureFpsRange[0] = supportedPreviewFpsRanges[i].low;
+            bestStillCaptureFpsRange[1] = supportedPreviewFpsRanges[i].high;
             curRange = nextRange;
         }
     }
@@ -2836,22 +2862,7 @@
 
 int64_t Parameters::getJpegStreamMinFrameDurationNs(Parameters::Size size) {
     if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
-        const int STREAM_DURATION_SIZE = 4;
-        const int STREAM_FORMAT_OFFSET = 0;
-        const int STREAM_WIDTH_OFFSET = 1;
-        const int STREAM_HEIGHT_OFFSET = 2;
-        const int STREAM_DURATION_OFFSET = 3;
-        camera_metadata_ro_entry_t availableStreamMinDurations =
-                    staticInfo(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
-        for (size_t i = 0; i < availableStreamMinDurations.count; i+= STREAM_DURATION_SIZE) {
-            int64_t format = availableStreamMinDurations.data.i64[i + STREAM_FORMAT_OFFSET];
-            int64_t width = availableStreamMinDurations.data.i64[i + STREAM_WIDTH_OFFSET];
-            int64_t height = availableStreamMinDurations.data.i64[i + STREAM_HEIGHT_OFFSET];
-            int64_t duration = availableStreamMinDurations.data.i64[i + STREAM_DURATION_OFFSET];
-            if (format == HAL_PIXEL_FORMAT_BLOB && width == size.width && height == size.height) {
-                return duration;
-            }
-        }
+        return getMinFrameDurationNs(size, HAL_PIXEL_FORMAT_BLOB);
     } else {
         Vector<Size> availableJpegSizes = getAvailableJpegSizes();
         size_t streamIdx = availableJpegSizes.size();
@@ -2875,6 +2886,57 @@
     return -1;
 }
 
+int64_t Parameters::getMinFrameDurationNs(Parameters::Size size, int fmt) {
+    if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
+        ALOGE("Min frame duration for HAL 3.1 or lower is not supported");
+        return -1;
+    }
+
+    const int STREAM_DURATION_SIZE = 4;
+    const int STREAM_FORMAT_OFFSET = 0;
+    const int STREAM_WIDTH_OFFSET = 1;
+    const int STREAM_HEIGHT_OFFSET = 2;
+    const int STREAM_DURATION_OFFSET = 3;
+    camera_metadata_ro_entry_t availableStreamMinDurations =
+                staticInfo(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+    for (size_t i = 0; i < availableStreamMinDurations.count; i+= STREAM_DURATION_SIZE) {
+        int64_t format = availableStreamMinDurations.data.i64[i + STREAM_FORMAT_OFFSET];
+        int64_t width = availableStreamMinDurations.data.i64[i + STREAM_WIDTH_OFFSET];
+        int64_t height = availableStreamMinDurations.data.i64[i + STREAM_HEIGHT_OFFSET];
+        int64_t duration = availableStreamMinDurations.data.i64[i + STREAM_DURATION_OFFSET];
+        if (format == fmt && width == size.width && height == size.height) {
+            return duration;
+        }
+    }
+
+    return -1;
+}
+
+bool Parameters::isFpsSupported(const Vector<Size> &sizes, int format, int32_t fps) {
+    // Skip the check for older HAL version, as the min duration is not supported.
+    if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
+        return true;
+    }
+
+    // Get min frame duration for each size and check if the given fps range can be supported.
+    const int32_t FPS_MARGIN = 1;
+    for (size_t i = 0 ; i < sizes.size(); i++) {
+        int64_t minFrameDuration = getMinFrameDurationNs(sizes[i], format);
+        if (minFrameDuration <= 0) {
+            ALOGE("Min frame duration (%" PRId64") for size (%dx%d) and format 0x%x is wrong!",
+                minFrameDuration, sizes[i].width, sizes[i].height, format);
+            return false;
+        }
+        int32_t maxSupportedFps = 1e9 / minFrameDuration;
+        // Add some margin here for the case where the hal supports 29.xxxfps.
+        maxSupportedFps += FPS_MARGIN;
+        if (fps > maxSupportedFps) {
+            return false;
+        }
+    }
+    return true;
+}
+
 SortedVector<int32_t> Parameters::getAvailableOutputFormats() {
     SortedVector<int32_t> outputFormats; // Non-duplicated output formats
     if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 798aab5..c8ecbba 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -115,6 +115,11 @@
         int32_t height;
     };
 
+    struct FpsRange {
+        int32_t low;
+        int32_t high;
+    };
+
     int32_t exposureCompensation;
     bool autoExposureLock;
     bool autoWhiteBalanceLock;
@@ -390,6 +395,15 @@
     // return -1 if input jpeg size cannot be found in supported size list
     int64_t getJpegStreamMinFrameDurationNs(Parameters::Size size);
 
+    // Helper function to get minimum frame duration for a size/format combination
+    // return -1 if input size/format combination cannot be found.
+    int64_t getMinFrameDurationNs(Parameters::Size size, int format);
+
+    // Helper function to check if a given fps is supported by all the sizes with
+    // the same format.
+    // return true if the device doesn't support min frame duration metadata tag.
+    bool isFpsSupported(const Vector<Size> &size, int format, int32_t fps);
+
     // Helper function to get non-duplicated available output formats
     SortedVector<int32_t> getAvailableOutputFormats();
     // Helper function to get available output jpeg sizes
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index b4f8e21..5166eb5 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -1210,6 +1210,14 @@
     }
 }
 
+void CameraDeviceClient::notifyRequestQueueEmpty() {
+    // Thread safe. Don't bother locking.
+    sp<hardware::camera2::ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+    if (remoteCb != 0) {
+        remoteCb->onRequestQueueEmpty();
+    }
+}
+
 void CameraDeviceClient::detachDevice() {
     if (mDevice == 0) return;
 
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index de283ea..68e453c 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -164,6 +164,7 @@
                              const CaptureResultExtras& resultExtras);
     virtual void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp);
     virtual void notifyPrepared(int streamId);
+    virtual void notifyRequestQueueEmpty();
     virtual void notifyRepeatingRequestError(long lastFrameNumber);
 
     /**
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index ccd1e4d..7e26153 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -307,6 +307,12 @@
 }
 
 template <typename TClientBase>
+void Camera2ClientBase<TClientBase>::notifyRequestQueueEmpty() {
+
+    ALOGV("%s: Request queue now empty", __FUNCTION__);
+}
+
+template <typename TClientBase>
 void Camera2ClientBase<TClientBase>::notifyRepeatingRequestError(long lastFrameNumber) {
     (void)lastFrameNumber;
 
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index dbbf638..9fd0a78 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -73,6 +73,7 @@
     virtual void          notifyAutoWhitebalance(uint8_t newState,
                                                  int triggerId);
     virtual void          notifyPrepared(int streamId);
+    virtual void          notifyRequestQueueEmpty();
     virtual void          notifyRepeatingRequestError(long lastFrameNumber);
 
     int                   getCameraId() const;
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 984d84b..f30afe3 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -204,6 +204,7 @@
         virtual void notifyShutter(const CaptureResultExtras &resultExtras,
                 nsecs_t timestamp) = 0;
         virtual void notifyPrepared(int streamId) = 0;
+        virtual void notifyRequestQueueEmpty() = 0;
 
         // Required only for API1
         virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 73a4124..2f3251f 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -686,7 +686,8 @@
 }
 
 status_t Camera3Device::convertMetadataListToRequestListLocked(
-        const List<const CameraMetadata> &metadataList, RequestList *requestList) {
+        const List<const CameraMetadata> &metadataList, bool repeating,
+        RequestList *requestList) {
     if (requestList == NULL) {
         CLOGE("requestList cannot be NULL.");
         return BAD_VALUE;
@@ -701,6 +702,8 @@
             return BAD_VALUE;
         }
 
+        newRequest->mRepeating = repeating;
+
         // Setup burst Id and request Id
         newRequest->mResultExtras.burstId = burstId++;
         if (it->exists(ANDROID_REQUEST_ID)) {
@@ -757,7 +760,8 @@
 
     RequestList requestList;
 
-    res = convertMetadataListToRequestListLocked(requests, /*out*/&requestList);
+    res = convertMetadataListToRequestListLocked(requests, repeating,
+            /*out*/&requestList);
     if (res != OK) {
         // error logged by previous call
         return res;
@@ -3535,6 +3539,12 @@
                 mRequestQueue.begin();
         nextRequest = *firstRequest;
         mRequestQueue.erase(firstRequest);
+        if (mRequestQueue.empty() && !nextRequest->mRepeating) {
+            sp<NotificationListener> listener = mListener.promote();
+            if (listener != NULL) {
+                listener->notifyRequestQueueEmpty();
+            }
+        }
     }
 
     // In case we've been unpaused by setPaused clearing mDoPause, need to
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 33429a6..31901bc 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -297,6 +297,8 @@
         // requests will be submitted to HAL at a time. The batch size for
         // the following 7 requests will be ignored by the request thread.
         int                                 mBatchSize;
+        //  Whether this request is from a repeating or repeating burst.
+        bool                                mRepeating;
     };
     typedef List<sp<CaptureRequest> > RequestList;
 
@@ -304,6 +306,7 @@
 
     status_t convertMetadataListToRequestListLocked(
             const List<const CameraMetadata> &metadataList,
+            bool repeating,
             /*out*/
             RequestList *requestList);
 
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index 8baaf13..4ce5c38 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -27,14 +27,8 @@
     liblog \
     libmedia \
     libmediadrm \
-    libmediaplayerservice \
-    libstagefright \
-    libui \
     libutils \
 
-LOCAL_C_INCLUDES := \
-    frameworks/av/media/libmediaplayerservice \
-
 LOCAL_CFLAGS += -Wall -Wextra -Werror
 
 LOCAL_MODULE:= mediadrmserver
diff --git a/services/radio/Android.mk b/services/radio/Android.mk
index f5d74d3..219c372 100644
--- a/services/radio/Android.mk
+++ b/services/radio/Android.mk
@@ -18,10 +18,10 @@
 
 
 LOCAL_SRC_FILES:=               \
-    RadioService.cpp
+    RadioService.cpp \
+    RadioHalLegacy.cpp
 
 LOCAL_SHARED_LIBRARIES:= \
-    libui \
     liblog \
     libutils \
     libbinder \
diff --git a/services/radio/RadioHalLegacy.cpp b/services/radio/RadioHalLegacy.cpp
new file mode 100644
index 0000000..d50ccd4
--- /dev/null
+++ b/services/radio/RadioHalLegacy.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RadioHalLegacy"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/misc.h>
+#include "RadioHalLegacy.h"
+
+namespace android {
+
+const char *RadioHalLegacy::sClassModuleNames[] = {
+    RADIO_HARDWARE_MODULE_ID_FM, /* corresponds to RADIO_CLASS_AM_FM */
+    RADIO_HARDWARE_MODULE_ID_SAT,  /* corresponds to RADIO_CLASS_SAT */
+    RADIO_HARDWARE_MODULE_ID_DT,   /* corresponds to RADIO_CLASS_DT */
+};
+
+/* static */
+sp<RadioInterface> RadioInterface::connectModule(radio_class_t classId)
+{
+    return new RadioHalLegacy(classId);
+}
+
+RadioHalLegacy::RadioHalLegacy(radio_class_t classId)
+    : RadioInterface(), mClassId(classId), mHwDevice(NULL)
+{
+}
+
+void RadioHalLegacy::onFirstRef()
+{
+    const hw_module_t *mod;
+    int rc;
+    ALOGI("%s mClassId %d", __FUNCTION__, mClassId);
+
+    mHwDevice = NULL;
+
+    if ((mClassId < 0) ||
+            (mClassId >= NELEM(sClassModuleNames))) {
+        ALOGE("invalid class ID %d", mClassId);
+        return;
+    }
+
+    ALOGI("%s RADIO_HARDWARE_MODULE_ID %s %s",
+          __FUNCTION__, RADIO_HARDWARE_MODULE_ID, sClassModuleNames[mClassId]);
+
+    rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, sClassModuleNames[mClassId], &mod);
+    if (rc != 0) {
+        ALOGE("couldn't load radio module %s.%s (%s)",
+              RADIO_HARDWARE_MODULE_ID, sClassModuleNames[mClassId], strerror(-rc));
+        return;
+    }
+    rc = radio_hw_device_open(mod, &mHwDevice);
+    if (rc != 0) {
+        ALOGE("couldn't open radio hw device in %s.%s (%s)",
+              RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
+        mHwDevice = NULL;
+        return;
+    }
+    if (mHwDevice->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
+        ALOGE("wrong radio hw device version %04x", mHwDevice->common.version);
+        radio_hw_device_close(mHwDevice);
+        mHwDevice = NULL;
+    }
+}
+
+RadioHalLegacy::~RadioHalLegacy()
+{
+    if (mHwDevice != NULL) {
+        radio_hw_device_close(mHwDevice);
+    }
+}
+
+int RadioHalLegacy::getProperties(radio_hal_properties_t *properties)
+{
+    if (mHwDevice == NULL) {
+        return -ENODEV;
+    }
+
+    int rc = mHwDevice->get_properties(mHwDevice, properties);
+    if (rc != 0) {
+        ALOGE("could not read implementation properties");
+    }
+
+    return rc;
+}
+
+int RadioHalLegacy::openTuner(const radio_hal_band_config_t *config,
+                bool audio,
+                sp<TunerCallbackInterface> callback,
+                sp<TunerInterface>& tuner)
+{
+    if (mHwDevice == NULL) {
+        return -ENODEV;
+    }
+    sp<Tuner> tunerImpl = new Tuner(callback);
+
+    const struct radio_tuner *halTuner;
+    int rc = mHwDevice->open_tuner(mHwDevice, config, audio,
+                                   RadioHalLegacy::Tuner::callback, tunerImpl.get(),
+                                   &halTuner);
+    if (rc == 0) {
+        tunerImpl->setHalTuner(halTuner);
+        tuner = tunerImpl;
+    }
+    return rc;
+}
+
+int RadioHalLegacy::closeTuner(sp<TunerInterface>& tuner)
+{
+    if (mHwDevice == NULL) {
+        return -ENODEV;
+    }
+    if (tuner == 0) {
+        return -EINVAL;
+    }
+    sp<Tuner> tunerImpl = (Tuner *)tuner.get();
+    return mHwDevice->close_tuner(mHwDevice, tunerImpl->getHalTuner());
+}
+
+int RadioHalLegacy::Tuner::setConfiguration(const radio_hal_band_config_t *config)
+{
+    if (mHalTuner == NULL) {
+        return -ENODEV;
+    }
+    return mHalTuner->set_configuration(mHalTuner, config);
+}
+
+int RadioHalLegacy::Tuner::getConfiguration(radio_hal_band_config_t *config)
+{
+    if (mHalTuner == NULL) {
+        return -ENODEV;
+    }
+    return mHalTuner->get_configuration(mHalTuner, config);
+}
+
+int RadioHalLegacy::Tuner::scan(radio_direction_t direction, bool skip_sub_channel)
+{
+    if (mHalTuner == NULL) {
+        return -ENODEV;
+    }
+    return mHalTuner->scan(mHalTuner, direction, skip_sub_channel);
+}
+
+int RadioHalLegacy::Tuner::step(radio_direction_t direction, bool skip_sub_channel)
+{
+    if (mHalTuner == NULL) {
+        return -ENODEV;
+    }
+    return mHalTuner->step(mHalTuner, direction, skip_sub_channel);
+}
+
+int RadioHalLegacy::Tuner::tune(unsigned int channel, unsigned int sub_channel)
+{
+    if (mHalTuner == NULL) {
+        return -ENODEV;
+    }
+    return mHalTuner->tune(mHalTuner, channel, sub_channel);
+}
+
+int RadioHalLegacy::Tuner::cancel()
+{
+    if (mHalTuner == NULL) {
+        return -ENODEV;
+    }
+    return mHalTuner->cancel(mHalTuner);
+}
+
+int RadioHalLegacy::Tuner::getProgramInformation(radio_program_info_t *info)
+{
+    if (mHalTuner == NULL) {
+        return -ENODEV;
+    }
+    return mHalTuner->get_program_information(mHalTuner, info);
+}
+
+void RadioHalLegacy::Tuner::onCallback(radio_hal_event_t *halEvent)
+{
+    if (mCallback != 0) {
+        mCallback->onEvent(halEvent);
+    }
+}
+
+//static
+void RadioHalLegacy::Tuner::callback(radio_hal_event_t *halEvent, void *cookie)
+{
+    wp<RadioHalLegacy::Tuner> weak = wp<RadioHalLegacy::Tuner>((RadioHalLegacy::Tuner *)cookie);
+    sp<RadioHalLegacy::Tuner> tuner = weak.promote();
+    if (tuner != 0) {
+        tuner->onCallback(halEvent);
+    }
+}
+
+RadioHalLegacy::Tuner::Tuner(sp<TunerCallbackInterface> callback)
+    : TunerInterface(), mHalTuner(NULL), mCallback(callback)
+{
+}
+
+
+RadioHalLegacy::Tuner::~Tuner()
+{
+}
+
+
+} // namespace android
diff --git a/services/radio/RadioHalLegacy.h b/services/radio/RadioHalLegacy.h
new file mode 100644
index 0000000..7d4831b
--- /dev/null
+++ b/services/radio/RadioHalLegacy.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_RADIO_HAL_LEGACY_H
+#define ANDROID_HARDWARE_RADIO_HAL_LEGACY_H
+
+#include <utils/RefBase.h>
+#include <hardware/radio.h>
+#include "RadioInterface.h"
+#include "TunerInterface.h"
+#include "TunerCallbackInterface.h"
+
+namespace android {
+
+class RadioHalLegacy : public RadioInterface
+{
+public:
+        RadioHalLegacy(radio_class_t classId);
+
+        // RadioInterface
+        virtual int getProperties(radio_hal_properties_t *properties);
+        virtual int openTuner(const radio_hal_band_config_t *config,
+                        bool audio,
+                        sp<TunerCallbackInterface> callback,
+                        sp<TunerInterface>& tuner);
+        virtual int closeTuner(sp<TunerInterface>& tuner);
+
+        // RefBase
+        virtual void onFirstRef();
+
+        class Tuner  : public TunerInterface
+        {
+        public:
+                        Tuner(sp<TunerCallbackInterface> callback);
+
+            virtual int setConfiguration(const radio_hal_band_config_t *config);
+            virtual int getConfiguration(radio_hal_band_config_t *config);
+            virtual int scan(radio_direction_t direction, bool skip_sub_channel);
+            virtual int step(radio_direction_t direction, bool skip_sub_channel);
+            virtual int tune(unsigned int channel, unsigned int sub_channel);
+            virtual int cancel();
+            virtual int getProgramInformation(radio_program_info_t *info);
+
+            static void callback(radio_hal_event_t *halEvent, void *cookie);
+                   void onCallback(radio_hal_event_t *halEvent);
+
+            void setHalTuner(const struct radio_tuner *halTuner) { mHalTuner = halTuner; }
+            const struct radio_tuner *getHalTuner() { return mHalTuner; }
+
+        private:
+            virtual      ~Tuner();
+
+            const struct radio_tuner    *mHalTuner;
+            sp<TunerCallbackInterface>  mCallback;
+        };
+
+protected:
+        virtual     ~RadioHalLegacy();
+
+private:
+        static const char * sClassModuleNames[];
+
+        radio_class_t mClassId;
+        struct radio_hw_device  *mHwDevice;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_HAL_LEGACY_H
diff --git a/services/radio/RadioInterface.h b/services/radio/RadioInterface.h
new file mode 100644
index 0000000..fcfb4d5
--- /dev/null
+++ b/services/radio/RadioInterface.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_RADIO_INTERFACE_H
+#define ANDROID_HARDWARE_RADIO_INTERFACE_H
+
+#include <utils/RefBase.h>
+#include <system/radio.h>
+#include "TunerInterface.h"
+#include "TunerCallbackInterface.h"
+
+namespace android {
+
+class RadioInterface : public virtual RefBase
+{
+public:
+        /* get a sound trigger HAL instance */
+        static sp<RadioInterface> connectModule(radio_class_t classId);
+
+        /*
+         * Retrieve implementation properties.
+         *
+         * arguments:
+         * - properties: where to return the module properties
+         *
+         * returns:
+         *  0 if no error
+         *  -EINVAL if invalid arguments are passed
+         */
+        virtual int getProperties(radio_hal_properties_t *properties) = 0;
+
+        /*
+         * Open a tuner interface for the requested configuration.
+         * If no other tuner is opened, this will activate the radio module.
+         *
+         * arguments:
+         * - config: the band configuration to apply
+         * - audio: this tuner will be used for live radio listening and should be connected to
+         * the radio audio source.
+         * - callback: the event callback
+         * - cookie: the cookie to pass when calling the callback
+         * - tuner: where to return the tuner interface
+         *
+         * returns:
+         *  0 if HW was powered up and configuration could be applied
+         *  -EINVAL if configuration requested is invalid
+         *  -ENOSYS if called out of sequence
+         *
+         * Callback function with event RADIO_EVENT_CONFIG MUST be called once the
+         * configuration is applied or a failure occurs or after a time out.
+         */
+        virtual int openTuner(const radio_hal_band_config_t *config,
+                        bool audio,
+                        sp<TunerCallbackInterface> callback,
+                        sp<TunerInterface>& tuner) = 0;
+
+        /*
+         * Close a tuner interface.
+         * If the last tuner is closed, the radio module is deactivated.
+         *
+         * arguments:
+         * - tuner: the tuner interface to close
+         *
+         * returns:
+         *  0 if powered down successfully.
+         *  -EINVAL if an invalid argument is passed
+         *  -ENOSYS if called out of sequence
+         */
+        virtual int closeTuner(sp<TunerInterface>& tuner) = 0;
+
+protected:
+        RadioInterface() {}
+        virtual     ~RadioInterface() {}
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_INTERFACE_H
diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp
index 5a3f750..a73ed8f 100644
--- a/services/radio/RadioService.cpp
+++ b/services/radio/RadioService.cpp
@@ -51,31 +51,15 @@
 
 void RadioService::onFirstRef()
 {
-    const hw_module_t *mod;
-    int rc;
-    struct radio_hw_device *dev;
-
     ALOGI("%s", __FUNCTION__);
 
-    rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, RADIO_HARDWARE_MODULE_ID_FM, &mod);
-    if (rc != 0) {
-        ALOGE("couldn't load radio module %s.%s (%s)",
-              RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
-        return;
-    }
-    rc = radio_hw_device_open(mod, &dev);
-    if (rc != 0) {
-        ALOGE("couldn't open radio hw device in %s.%s (%s)",
-              RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
-        return;
-    }
-    if (dev->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
-        ALOGE("wrong radio hw device version %04x", dev->common.version);
-        return;
-    }
+    sp<RadioInterface> dev = RadioInterface::connectModule(RADIO_CLASS_AM_FM);
 
+    if (dev == 0) {
+        return;
+    }
     struct radio_hal_properties halProperties;
-    rc = dev->get_properties(dev, &halProperties);
+    int rc = dev->getProperties(&halProperties);
     if (rc != 0) {
         ALOGE("could not read implementation properties");
         return;
@@ -94,9 +78,6 @@
 
 RadioService::~RadioService()
 {
-    for (size_t i = 0; i < mModules.size(); i++) {
-        radio_hw_device_close(mModules.valueAt(i)->hwDevice());
-    }
 }
 
 status_t RadioService::listModules(struct radio_properties *properties,
@@ -108,7 +89,7 @@
     if (numModules == NULL || (*numModules != 0 && properties == NULL)) {
         return BAD_VALUE;
     }
-    size_t maxModules = *numModules;
+    uint32_t maxModules = *numModules;
     *numModules = mModules.size();
     for (size_t i = 0; i < mModules.size() && i < maxModules; i++) {
         properties[i] = mModules.valueAt(i)->properties();
@@ -192,16 +173,6 @@
 }
 
 
-// static
-void RadioService::callback(radio_hal_event_t *halEvent, void *cookie)
-{
-    CallbackThread *callbackThread = (CallbackThread *)cookie;
-    if (callbackThread == NULL) {
-        return;
-    }
-    callbackThread->sendEvent(halEvent);
-}
-
 /* static */
 void RadioService::convertProperties(radio_properties_t *properties,
                                      const radio_hal_properties_t *halProperties)
@@ -305,32 +276,40 @@
 {
     sp<IMemory> eventMemory;
 
-    size_t headerSize =
-            (sizeof(struct radio_event) + sizeof(unsigned int) - 1) /sizeof(unsigned int);
-    size_t metadataSize = 0;
+    // The event layout in shared memory is:
+    // sizeof(struct radio_event) bytes : the event itself
+    // 4 bytes                          : metadata size or 0
+    // N bytes                          : metadata if present
+    uint32_t metadataOffset = sizeof(struct radio_event) + sizeof(uint32_t);
+    uint32_t metadataSize = 0;
+
     switch (halEvent->type) {
     case RADIO_EVENT_TUNED:
     case RADIO_EVENT_AF_SWITCH:
         if (radio_metadata_check(halEvent->info.metadata) == 0) {
-            metadataSize = radio_metadata_get_size(halEvent->info.metadata);
+            metadataSize = (uint32_t)radio_metadata_get_size(halEvent->info.metadata);
         }
         break;
     case RADIO_EVENT_METADATA:
         if (radio_metadata_check(halEvent->metadata) != 0) {
             return eventMemory;
         }
-        metadataSize = radio_metadata_get_size(halEvent->metadata);
+        metadataSize = (uint32_t)radio_metadata_get_size(halEvent->metadata);
         break;
     default:
         break;
     }
-    size_t size = headerSize + metadataSize;
-    eventMemory = mMemoryDealer->allocate(size);
+
+    eventMemory = mMemoryDealer->allocate(metadataOffset + metadataSize);
     if (eventMemory == 0 || eventMemory->pointer() == NULL) {
         eventMemory.clear();
         return eventMemory;
     }
+
     struct radio_event *event = (struct radio_event *)eventMemory->pointer();
+
+    *(uint32_t *)((uint8_t *)event + metadataOffset - sizeof(uint32_t)) = metadataSize;
+
     event->type = halEvent->type;
     event->status = halEvent->status;
 
@@ -342,10 +321,7 @@
     case RADIO_EVENT_AF_SWITCH:
         event->info = halEvent->info;
         if (metadataSize != 0) {
-            memcpy((char *)event + headerSize, halEvent->info.metadata, metadataSize);
-            // replace meta data pointer by offset while in shared memory so that receiving side
-            // can restore the pointer in destination process.
-            event->info.metadata = (radio_metadata_t *)headerSize;
+            memcpy((uint8_t *)event + metadataOffset, halEvent->info.metadata, metadataSize);
         }
         break;
     case RADIO_EVENT_TA:
@@ -355,10 +331,9 @@
         event->on = halEvent->on;
         break;
     case RADIO_EVENT_METADATA:
-        memcpy((char *)event + headerSize, halEvent->metadata, metadataSize);
-        // replace meta data pointer by offset while in shared memory so that receiving side
-        // can restore the pointer in destination process.
-        event->metadata = (radio_metadata_t *)headerSize;
+        if (metadataSize != 0) {
+            memcpy((uint8_t *)event + metadataOffset, halEvent->metadata, metadataSize);
+        }
         break;
     case RADIO_EVENT_HW_FAILURE:
     default:
@@ -385,12 +360,13 @@
 #undef LOG_TAG
 #define LOG_TAG "RadioService::Module"
 
-RadioService::Module::Module(radio_hw_device* hwDevice, radio_properties properties)
+RadioService::Module::Module(sp<RadioInterface> hwDevice, radio_properties properties)
  : mHwDevice(hwDevice), mProperties(properties), mMute(true)
 {
 }
 
 RadioService::Module::~Module() {
+    mHwDevice.clear();
     mModuleClients.clear();
 }
 
@@ -404,10 +380,15 @@
                                     bool audio)
 {
     ALOGV("addClient() %p config %p product %s", this, config, mProperties.product);
+
     AutoMutex lock(mLock);
     sp<ModuleClient> moduleClient;
     int ret;
 
+    if (mHwDevice == 0) {
+        return moduleClient;
+    }
+
     for (size_t i = 0; i < mModuleClients.size(); i++) {
         if (mModuleClients[i]->client() == client) {
             // client already connected: reject
@@ -464,7 +445,7 @@
         }
     }
 
-    const struct radio_tuner *halTuner;
+    sp<TunerInterface> halTuner;
     sp<ModuleClient> preemtedClient;
     if (audio) {
         if (allocatedAudio >= mProperties.num_audio_sources) {
@@ -484,18 +465,19 @@
     }
     if (preemtedClient != 0) {
         halTuner = preemtedClient->getTuner();
-        preemtedClient->setTuner(NULL);
-        mHwDevice->close_tuner(mHwDevice, halTuner);
+        sp<TunerInterface> clear;
+        preemtedClient->setTuner(clear);
+        mHwDevice->closeTuner(halTuner);
         if (preemtedClient->audio()) {
             notifyDeviceConnection(false, "");
         }
     }
 
-    ret = mHwDevice->open_tuner(mHwDevice, &halConfig, audio,
-                                RadioService::callback, moduleClient->callbackThread().get(),
-                                &halTuner);
+    ret = mHwDevice->openTuner(&halConfig, audio,
+                               moduleClient,
+                               halTuner);
     if (ret == 0) {
-        ALOGV("addClient() setTuner %p", halTuner);
+        ALOGV("addClient() setTuner %p", halTuner.get());
         moduleClient->setTuner(halTuner);
         mModuleClients.add(moduleClient);
         if (audio) {
@@ -527,12 +509,15 @@
     }
 
     mModuleClients.removeAt(index);
-    const struct radio_tuner *halTuner = moduleClient->getTuner();
+    sp<TunerInterface> halTuner = moduleClient->getTuner();
     if (halTuner == NULL) {
         return;
     }
 
-    mHwDevice->close_tuner(mHwDevice, halTuner);
+    if (mHwDevice != 0) {
+        mHwDevice->closeTuner(halTuner);
+    }
+
     if (moduleClient->audio()) {
         notifyDeviceConnection(false, "");
     }
@@ -543,6 +528,10 @@
         return;
     }
 
+    if (mHwDevice == 0) {
+        return;
+    }
+
     // Tuner reallocation logic:
     // When a client is removed and was controlling a tuner, this tuner will be allocated to a
     // previously preempted client. This client will be notified by a callback with
@@ -591,9 +580,9 @@
     ALOG_ASSERT(youngestClient != 0, "removeClient() removed client no candidate found for tuner");
 
     struct radio_hal_band_config halConfig = youngestClient->halConfig();
-    ret = mHwDevice->open_tuner(mHwDevice, &halConfig, youngestClient->audio(),
-                                RadioService::callback, moduleClient->callbackThread().get(),
-                                &halTuner);
+    ret = mHwDevice->openTuner(&halConfig, youngestClient->audio(),
+                                moduleClient,
+                                halTuner);
 
     if (ret == 0) {
         youngestClient->setTuner(halTuner);
@@ -646,7 +635,7 @@
                                          const sp<IRadioClient>& client,
                                          const struct radio_band_config *config,
                                          bool audio)
- : mModule(module), mClient(client), mConfig(*config), mAudio(audio), mTuner(NULL)
+ : mModule(module), mClient(client), mConfig(*config), mAudio(audio), mTuner(0)
 {
 }
 
@@ -666,6 +655,11 @@
     }
 }
 
+void RadioService::ModuleClient::onEvent(radio_hal_event_t *halEvent)
+{
+    mCallbackThread->sendEvent(halEvent);
+}
+
 status_t RadioService::ModuleClient::dump(int fd __unused,
                                              const Vector<String16>& args __unused) {
     String8 result;
@@ -696,14 +690,14 @@
     return mConfig.band;
 }
 
-const struct radio_tuner *RadioService::ModuleClient::getTuner() const
+sp<TunerInterface>& RadioService::ModuleClient::getTuner()
 {
     AutoMutex lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     return mTuner;
 }
 
-void RadioService::ModuleClient::setTuner(const struct radio_tuner *tuner)
+void RadioService::ModuleClient::setTuner(sp<TunerInterface>& tuner)
 {
     ALOGV("%s %p", __FUNCTION__, this);
 
@@ -714,7 +708,7 @@
     radio_hal_event_t event;
     event.type = RADIO_EVENT_CONTROL;
     event.status = 0;
-    event.on = mTuner != NULL;
+    event.on = mTuner != 0;
     mCallbackThread->sendEvent(&event);
     ALOGV("%s DONE", __FUNCTION__);
 
@@ -726,10 +720,10 @@
     status_t status = NO_ERROR;
     ALOGV("%s locked", __FUNCTION__);
 
-    if (mTuner != NULL) {
+    if (mTuner != 0) {
         struct radio_hal_band_config halConfig;
         halConfig = config->band;
-        status = (status_t)mTuner->set_configuration(mTuner, &halConfig);
+        status = (status_t)mTuner->setConfiguration(&halConfig);
         if (status == NO_ERROR) {
             mConfig = *config;
         }
@@ -747,9 +741,9 @@
     status_t status = NO_ERROR;
     ALOGV("%s locked", __FUNCTION__);
 
-    if (mTuner != NULL) {
+    if (mTuner != 0) {
         struct radio_hal_band_config halConfig;
-        status = (status_t)mTuner->get_configuration(mTuner, &halConfig);
+        status = (status_t)mTuner->getConfiguration(&halConfig);
         if (status == NO_ERROR) {
             mConfig.band = halConfig;
         }
@@ -765,7 +759,7 @@
     {
         Mutex::Autolock _l(mLock);
         ALOGV("%s locked", __FUNCTION__);
-        if (mTuner == NULL || !mAudio) {
+        if (mTuner == 0 || !mAudio) {
             return INVALID_OPERATION;
         }
         module = mModule.promote();
@@ -796,8 +790,8 @@
     AutoMutex lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     status_t status;
-    if (mTuner != NULL) {
-        status = (status_t)mTuner->scan(mTuner, direction, skipSubChannel);
+    if (mTuner != 0) {
+        status = (status_t)mTuner->scan(direction, skipSubChannel);
     } else {
         status = INVALID_OPERATION;
     }
@@ -809,21 +803,21 @@
     AutoMutex lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     status_t status;
-    if (mTuner != NULL) {
-        status = (status_t)mTuner->step(mTuner, direction, skipSubChannel);
+    if (mTuner != 0) {
+        status = (status_t)mTuner->step(direction, skipSubChannel);
     } else {
         status = INVALID_OPERATION;
     }
     return status;
 }
 
-status_t RadioService::ModuleClient::tune(unsigned int channel, unsigned int subChannel)
+status_t RadioService::ModuleClient::tune(uint32_t channel, uint32_t subChannel)
 {
     AutoMutex lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     status_t status;
-    if (mTuner != NULL) {
-        status = (status_t)mTuner->tune(mTuner, channel, subChannel);
+    if (mTuner != 0) {
+        status = (status_t)mTuner->tune(channel, subChannel);
     } else {
         status = INVALID_OPERATION;
     }
@@ -835,8 +829,8 @@
     AutoMutex lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
     status_t status;
-    if (mTuner != NULL) {
-        status = (status_t)mTuner->cancel(mTuner);
+    if (mTuner != 0) {
+        status = (status_t)mTuner->cancel();
     } else {
         status = INVALID_OPERATION;
     }
@@ -849,10 +843,11 @@
     ALOGV("%s locked", __FUNCTION__);
     status_t status;
     if (mTuner != NULL) {
-        status = (status_t)mTuner->get_program_information(mTuner, info);
+        status = (status_t)mTuner->getProgramInformation(info);
     } else {
         status = INVALID_OPERATION;
     }
+
     return status;
 }
 
@@ -860,7 +855,7 @@
 {
     Mutex::Autolock lock(mLock);
     ALOGV("%s locked", __FUNCTION__);
-    *hasControl = mTuner != NULL;
+    *hasControl = mTuner != 0;
     return NO_ERROR;
 }
 
diff --git a/services/radio/RadioService.h b/services/radio/RadioService.h
index ac3481e..444eb7a 100644
--- a/services/radio/RadioService.h
+++ b/services/radio/RadioService.h
@@ -27,6 +27,9 @@
 #include <radio/IRadioClient.h>
 #include <system/radio.h>
 #include <hardware/radio.h>
+#include "RadioInterface.h"
+#include "TunerInterface.h"
+#include "TunerCallbackInterface.h"
 
 namespace android {
 
@@ -66,7 +69,7 @@
     class Module : public virtual RefBase {
     public:
 
-       Module(radio_hw_device* hwDevice,
+       Module(sp<RadioInterface> hwDevice,
               struct radio_properties properties);
 
        virtual ~Module();
@@ -83,7 +86,7 @@
 
        virtual status_t dump(int fd, const Vector<String16>& args);
 
-       const struct radio_hw_device *hwDevice() const { return mHwDevice; }
+       sp<RadioInterface> hwDevice() const { return mHwDevice; }
        const struct radio_properties properties() const { return mProperties; }
        const struct radio_band_config *getDefaultConfig() const ;
 
@@ -92,7 +95,7 @@
        void notifyDeviceConnection(bool connected, const char *address);
 
         Mutex                         mLock;          // protects  mModuleClients
-        const struct radio_hw_device  *mHwDevice;     // HAL hardware device
+        sp<RadioInterface>            mHwDevice;      // HAL hardware device
         const struct radio_properties mProperties;    // cached hardware module properties
         Vector< sp<ModuleClient> >    mModuleClients; // list of attached clients
         bool                          mMute;          // radio audio source state
@@ -128,7 +131,8 @@
     }; // class CallbackThread
 
     class ModuleClient : public BnRadio,
-                   public IBinder::DeathRecipient {
+                   public IBinder::DeathRecipient,
+                   public TunerCallbackInterface {
     public:
 
        ModuleClient(const sp<Module>& module,
@@ -167,8 +171,8 @@
                wp<Module> module() const { return mModule; }
                radio_hal_band_config_t halConfig() const;
                sp<CallbackThread> callbackThread() const { return mCallbackThread; }
-               void setTuner(const struct radio_tuner *tuner);
-               const struct radio_tuner *getTuner() const;
+               void setTuner(sp<TunerInterface>& tuner);
+               sp<TunerInterface>& getTuner();
                bool audio() const { return mAudio; }
 
                void onCallbackEvent(const sp<IMemory>& event);
@@ -179,6 +183,9 @@
        // IBinder::DeathRecipient implementation
        virtual void        binderDied(const wp<IBinder> &who);
 
+       // TunerCallbackInterface
+       virtual void onEvent(radio_hal_event_t *event);
+
     private:
 
         mutable Mutex               mLock;           // protects mClient, mConfig and mTuner
@@ -187,7 +194,7 @@
         radio_band_config_t         mConfig;         // current band configuration
         sp<CallbackThread>          mCallbackThread; // event callback thread
         const bool                  mAudio;
-        const struct radio_tuner    *mTuner;        // HAL tuner interface. NULL indicates that
+        sp<TunerInterface>          mTuner;        // HAL tuner interface. NULL indicates that
                                                     // this client does not have control on any
                                                     // tuner
     }; // class ModuleClient
diff --git a/services/radio/TunerCallbackInterface.h b/services/radio/TunerCallbackInterface.h
new file mode 100644
index 0000000..4973cce
--- /dev/null
+++ b/services/radio/TunerCallbackInterface.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_TUNER_CALLBACK_INTERFACE_H
+#define ANDROID_HARDWARE_TUNER_CALLBACK_INTERFACE_H
+
+#include <utils/RefBase.h>
+#include <system/radio.h>
+
+namespace android {
+
+class TunerCallbackInterface : public virtual RefBase
+{
+public:
+    virtual void onEvent(radio_hal_event_t *event) = 0;
+
+protected:
+                 TunerCallbackInterface() {}
+    virtual      ~TunerCallbackInterface() {}
+
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_TUNER_CALLBACK_INTERFACE_H
diff --git a/services/radio/TunerInterface.h b/services/radio/TunerInterface.h
new file mode 100644
index 0000000..4e657d3
--- /dev/null
+++ b/services/radio/TunerInterface.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_TUNER_INTERFACE_H
+#define ANDROID_HARDWARE_TUNER_INTERFACE_H
+
+#include <utils/RefBase.h>
+#include <system/radio.h>
+
+namespace android {
+
+class TunerInterface : public virtual RefBase
+{
+public:
+        /*
+         * Apply current radio band configuration (band, range, channel spacing ...).
+         *
+         * arguments:
+         * - config: the band configuration to apply
+         *
+         * returns:
+         *  0 if configuration could be applied
+         *  -EINVAL if configuration requested is invalid
+         *
+         * Automatically cancels pending scan, step or tune.
+         *
+         * Callback function with event RADIO_EVENT_CONFIG MUST be called once the
+         * configuration is applied or a failure occurs or after a time out.
+         */
+    virtual int setConfiguration(const radio_hal_band_config_t *config) = 0;
+
+        /*
+         * Retrieve current radio band configuration.
+         *
+         * arguments:
+         * - config: where to return the band configuration
+         *
+         * returns:
+         *  0 if valid configuration is returned
+         *  -EINVAL if invalid arguments are passed
+         */
+    virtual int getConfiguration(radio_hal_band_config_t *config) = 0;
+
+        /*
+         * Start scanning up to next valid station.
+         * Must be called when a valid configuration has been applied.
+         *
+         * arguments:
+         * - direction: RADIO_DIRECTION_UP or RADIO_DIRECTION_DOWN
+         * - skip_sub_channel: valid for HD radio or digital radios only: ignore sub channels
+         *  (e.g SPS for HD radio).
+         *
+         * returns:
+         *  0 if scan successfully started
+         *  -ENOSYS if called out of sequence
+         *  -ENODEV if another error occurs
+         *
+         * Automatically cancels pending scan, step or tune.
+         *
+         *  Callback function with event RADIO_EVENT_TUNED MUST be called once
+         *  locked on a station or after a time out or full frequency scan if
+         *  no station found. The event status should indicate if a valid station
+         *  is tuned or not.
+         */
+    virtual int scan(radio_direction_t direction, bool skip_sub_channel) = 0;
+
+        /*
+         * Move one channel spacing up or down.
+         * Must be called when a valid configuration has been applied.
+         *
+         * arguments:
+         * - direction: RADIO_DIRECTION_UP or RADIO_DIRECTION_DOWN
+         * - skip_sub_channel: valid for HD radio or digital radios only: ignore sub channels
+         *  (e.g SPS for HD radio).
+         *
+         * returns:
+         *  0 if step successfully started
+         *  -ENOSYS if called out of sequence
+         *  -ENODEV if another error occurs
+         *
+         * Automatically cancels pending scan, step or tune.
+         *
+         * Callback function with event RADIO_EVENT_TUNED MUST be called once
+         * step completed or after a time out. The event status should indicate
+         * if a valid station is tuned or not.
+         */
+    virtual int step(radio_direction_t direction, bool skip_sub_channel) = 0;
+
+        /*
+         * Tune to specified frequency.
+         * Must be called when a valid configuration has been applied.
+         *
+         * arguments:
+         * - channel: channel to tune to. A frequency in kHz for AM/FM/HD Radio bands.
+         * - sub_channel: valid for HD radio or digital radios only: (e.g SPS number for HD radio).
+         *
+         * returns:
+         *  0 if tune successfully started
+         *  -ENOSYS if called out of sequence
+         *  -EINVAL if invalid arguments are passed
+         *  -ENODEV if another error occurs
+         *
+         * Automatically cancels pending scan, step or tune.
+         *
+         * Callback function with event RADIO_EVENT_TUNED MUST be called once
+         * tuned or after a time out. The event status should indicate
+         * if a valid station is tuned or not.
+         */
+    virtual int tune(unsigned int channel, unsigned int sub_channel) = 0;
+
+        /*
+         * Cancel a scan, step or tune operation.
+         * Must be called while a scan, step or tune operation is pending
+         * (callback not yet sent).
+         *
+         * returns:
+         *  0 if successful
+         *  -ENOSYS if called out of sequence
+         *  -ENODEV if another error occurs
+         *
+         * The callback is not sent.
+         */
+    virtual int cancel() = 0;
+
+        /*
+         * Retrieve current station information.
+         *
+         * arguments:
+         * - info: where to return the program info.
+         * If info->metadata is NULL. no meta data should be returned.
+         * If meta data must be returned, they should be added to or cloned to
+         * info->metadata, not passed from a newly created meta data buffer.
+         *
+         * returns:
+         *  0 if tuned and information available
+         *  -EINVAL if invalid arguments are passed
+         *  -ENODEV if another error occurs
+         */
+    virtual int getProgramInformation(radio_program_info_t *info) = 0;
+
+protected:
+                TunerInterface() {}
+    virtual     ~TunerInterface() {}
+
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_TUNER_INTERFACE_H
diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk
index c55ac7f..2533132 100644
--- a/services/soundtrigger/Android.mk
+++ b/services/soundtrigger/Android.mk
@@ -16,7 +16,6 @@
 
 include $(CLEAR_VARS)
 
-
 ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1)
     LOCAL_CFLAGS += -DSOUND_TRIGGER_USE_STUB_MODULE
 endif
@@ -25,7 +24,6 @@
     SoundTriggerHwService.cpp
 
 LOCAL_SHARED_LIBRARIES:= \
-    libui \
     liblog \
     libutils \
     libbinder \
@@ -35,12 +33,32 @@
     libmedia \
     libserviceutility
 
+
+ifeq ($(ENABLE_TREBLE),true)
+# Treble configuration
+LOCAL_CFLAGS += -DENABLE_TREBLE
+LOCAL_SRC_FILES +=               \
+    SoundTriggerHalHidl.cpp
+
+LOCAL_SHARED_LIBRARIES += \
+		libhwbinder \
+		libhidl \
+		libbase \
+		android.hardware.soundtrigger@2.0 \
+		android.hardware.audio.common@2.0
+else
+# libhardware configuration
+LOCAL_SRC_FILES +=               \
+    SoundTriggerHalLegacy.cpp
+endif
+
+
 LOCAL_C_INCLUDES += \
     $(TOPDIR)frameworks/av/services/audioflinger
 
 LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
 
-LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS += -Wall -Werror
 
 LOCAL_MODULE:= libsoundtriggerservice
 
diff --git a/services/soundtrigger/SoundTriggerHalHidl.cpp b/services/soundtrigger/SoundTriggerHalHidl.cpp
new file mode 100644
index 0000000..e71d742
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalHidl.cpp
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SoundTriggerHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include "SoundTriggerHalHidl.h"
+#include <hidl/IServiceManager.h>
+#include <hwbinder/IPCThreadState.h>
+#include <hwbinder/ProcessState.h>
+
+namespace android {
+
+using android::hardware::Return;
+using android::hardware::ProcessState;
+using android::hardware::audio::common::V2_0::AudioDevice;
+
+pthread_once_t SoundTriggerHalHidl::sOnceControl = PTHREAD_ONCE_INIT;
+
+void SoundTriggerHalHidl::sOnceInit()
+{
+    ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ProcessState::self()->startThreadPool();
+}
+
+/* static */
+sp<SoundTriggerHalInterface> SoundTriggerHalInterface::connectModule(const char *moduleName)
+{
+    return new SoundTriggerHalHidl(moduleName);
+}
+
+int SoundTriggerHalHidl::getProperties(struct sound_trigger_properties *properties)
+{
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    ISoundTriggerHw::Properties halProperties;
+    Return<void> hidlReturn;
+    int32_t halReturn;
+    {
+        AutoMutex lock(mHalLock);
+        hidlReturn = soundtrigger->getProperties([&](int rc, auto res) {
+            halReturn = rc;
+            halProperties = res;
+            ALOGI("getProperties res implementor %s", res.implementor.c_str());
+        });
+    }
+
+    int ret = 0;
+    if (hidlReturn.getStatus().isOk()) {
+        convertPropertiesFromHal(properties, &halProperties);
+    } else {
+        ret = (int)hidlReturn.getStatus().transactionError();
+        if (ret == -EPIPE) {
+            clearService();
+        }
+    }
+
+    return ret;
+}
+
+int SoundTriggerHalHidl::loadSoundModel(struct sound_trigger_sound_model *sound_model,
+                        sound_model_callback_t callback,
+                        void *cookie,
+                        sound_model_handle_t *handle)
+{
+    if (handle == NULL) {
+        return -EINVAL;
+    }
+
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    uint32_t modelId;
+    {
+        AutoMutex lock(mLock);
+        do {
+            modelId = nextUniqueId();
+            ALOGI("loadSoundModel modelId %u", modelId);
+            sp<SoundModel> model = mSoundModels.valueFor(modelId);
+            ALOGI("loadSoundModel model %p", model.get());
+        } while (mSoundModels.valueFor(modelId) != 0 && modelId != 0);
+    }
+    LOG_ALWAYS_FATAL_IF(modelId == 0,
+                        "loadSoundModel(): wrap around in sound model IDs, num loaded models %zd",
+                        mSoundModels.size());
+
+    ISoundTriggerHw::SoundModel *halSoundModel =
+            convertSoundModelToHal(sound_model);
+    if (halSoundModel == NULL) {
+        return -EINVAL;
+    }
+
+    Return<void> hidlReturn;
+    int32_t halReturn;
+    SoundModelHandle halHandle;
+    {
+        AutoMutex lock(mHalLock);
+        if (sound_model->type == SOUND_MODEL_TYPE_KEYPHRASE) {
+            hidlReturn = soundtrigger->loadPhraseSoundModel(
+                    *(const ISoundTriggerHw::PhraseSoundModel *)halSoundModel,
+                    this, modelId, [&](int32_t retval, auto res) {
+                halReturn = retval;
+                halHandle = res;
+            });
+
+        } else {
+            hidlReturn = soundtrigger->loadSoundModel(*halSoundModel,
+                    this, modelId, [&](int32_t retval, auto res) {
+                halReturn = retval;
+                halHandle = res;
+            });
+        }
+    }
+
+    delete halSoundModel;
+
+    int ret = 0;
+    if (hidlReturn.getStatus().isOk()) {
+        AutoMutex lock(mLock);
+        *handle = (sound_model_handle_t)modelId;
+        sp<SoundModel> model = new SoundModel(*handle, callback, cookie, halHandle);
+        mSoundModels.add(*handle, model);
+    } else {
+        ret = (int)hidlReturn.getStatus().transactionError();
+        ALOGE("loadSoundModel error %d", ret);
+        if (ret == -EPIPE) {
+            clearService();
+        }
+    }
+
+
+    return ret;
+}
+
+int SoundTriggerHalHidl::unloadSoundModel(sound_model_handle_t handle)
+{
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    sp<SoundModel> model = removeModel(handle);
+    if (model == 0) {
+        ALOGE("unloadSoundModel model not found for handle %u", handle);
+        return -EINVAL;
+    }
+
+    Return<int32_t> halReturn(0);
+    {
+        AutoMutex lock(mHalLock);
+        halReturn = soundtrigger->unloadSoundModel(model->mHalHandle);
+    }
+
+    int ret = (int)halReturn.getStatus().transactionError();
+    ALOGE_IF(ret != 0, "unloadSoundModel error %d", ret);
+    if (ret == -EPIPE) {
+        clearService();
+    }
+
+    return ret;
+}
+
+int SoundTriggerHalHidl::startRecognition(sound_model_handle_t handle,
+                         const struct sound_trigger_recognition_config *config,
+                         recognition_callback_t callback,
+                         void *cookie)
+{
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    sp<SoundModel> model = getModel(handle);
+    if (model == 0) {
+        ALOGE("startRecognition model not found for handle %u", handle);
+        return -EINVAL;
+    }
+
+    model->mRecognitionCallback = callback;
+    model->mRecognitionCookie = cookie;
+
+    ISoundTriggerHw::RecognitionConfig *halConfig =
+            convertRecognitionConfigToHal(config);
+
+    Return<int32_t> halReturn(0);
+    {
+        AutoMutex lock(mHalLock);
+        halReturn = soundtrigger->startRecognition(model->mHalHandle, *halConfig, this, handle);
+    }
+
+    delete halConfig;
+
+    int ret = (int)halReturn.getStatus().transactionError();
+    ALOGE_IF(ret != 0, "startRecognition error %d", ret);
+    if (ret == -EPIPE) {
+        clearService();
+    }
+    return ret;
+}
+
+int SoundTriggerHalHidl::stopRecognition(sound_model_handle_t handle)
+{
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    sp<SoundModel> model = getModel(handle);
+    if (model == 0) {
+        ALOGE("stopRecognition model not found for handle %u", handle);
+        return -EINVAL;
+    }
+
+    Return<int32_t> halReturn(0);
+    {
+        AutoMutex lock(mHalLock);
+        halReturn = soundtrigger->stopRecognition(model->mHalHandle);
+    }
+
+    int ret = (int)halReturn.getStatus().transactionError();
+    ALOGE_IF(ret != 0, "stopRecognition error %d", ret);
+    if (ret == -EPIPE) {
+        clearService();
+    }
+    return ret;
+}
+
+int SoundTriggerHalHidl::stopAllRecognitions()
+{
+    sp<ISoundTriggerHw> soundtrigger = getService();
+    if (soundtrigger == 0) {
+        return -ENODEV;
+    }
+
+    Return<int32_t> halReturn(0);
+    {
+        AutoMutex lock(mHalLock);
+        Return<int32_t> halReturn = soundtrigger->stopAllRecognitions();
+    }
+
+    int ret = (int)halReturn.getStatus().transactionError();
+    ALOGE_IF(ret != 0, "stopAllRecognitions error %d", ret);
+    if (ret == -EPIPE) {
+        clearService();
+    }
+    return ret;
+}
+
+SoundTriggerHalHidl::SoundTriggerHalHidl(const char *moduleName)
+    : mModuleName(moduleName), mNextUniqueId(1)
+{
+}
+
+void SoundTriggerHalHidl::onFirstRef()
+{
+    pthread_once(&sOnceControl, &sOnceInit);
+}
+
+SoundTriggerHalHidl::~SoundTriggerHalHidl()
+{
+}
+
+sp<ISoundTriggerHw> SoundTriggerHalHidl::getService()
+{
+    AutoMutex lock(mLock);
+    if (mISoundTrigger == 0) {
+        if (mModuleName == NULL) {
+            mModuleName = "primary";
+        }
+        std::string serviceName = "sound_trigger.";
+        serviceName.append(mModuleName);
+        mISoundTrigger = ISoundTriggerHw::getService(serviceName);
+    }
+    return mISoundTrigger;
+}
+
+void SoundTriggerHalHidl::clearService()
+{
+    AutoMutex lock(mLock);
+    mISoundTrigger = 0;
+}
+
+sp<SoundTriggerHalHidl::SoundModel> SoundTriggerHalHidl::getModel(sound_model_handle_t handle)
+{
+    AutoMutex lock(mLock);
+    return mSoundModels.valueFor(handle);
+}
+
+sp<SoundTriggerHalHidl::SoundModel> SoundTriggerHalHidl::removeModel(sound_model_handle_t handle)
+{
+    AutoMutex lock(mLock);
+    sp<SoundModel> model = mSoundModels.valueFor(handle);
+    mSoundModels.removeItem(handle);
+    return model;
+}
+
+uint32_t SoundTriggerHalHidl::nextUniqueId()
+{
+    return (uint32_t) atomic_fetch_add_explicit(&mNextUniqueId,
+                (uint_fast32_t) 1, memory_order_acq_rel);
+}
+
+void SoundTriggerHalHidl::convertUuidToHal(Uuid *halUuid,
+                                           const sound_trigger_uuid_t *uuid)
+{
+    halUuid->timeLow = uuid->timeLow;
+    halUuid->timeMid = uuid->timeMid;
+    halUuid->versionAndTimeHigh = uuid->timeHiAndVersion;
+    halUuid->variantAndClockSeqHigh = uuid->clockSeq;
+    memcpy(halUuid->node.data(), &uuid->node[0], sizeof(uuid->node));
+}
+
+void SoundTriggerHalHidl::convertUuidFromHal(sound_trigger_uuid_t *uuid,
+                                             const Uuid *halUuid)
+{
+    uuid->timeLow = halUuid->timeLow;
+    uuid->timeMid = halUuid->timeMid;
+    uuid->timeHiAndVersion = halUuid->versionAndTimeHigh;
+    uuid->clockSeq = halUuid->variantAndClockSeqHigh;
+    memcpy(&uuid->node[0], halUuid->node.data(), sizeof(uuid->node));
+}
+
+void SoundTriggerHalHidl::convertPropertiesFromHal(
+        struct sound_trigger_properties *properties,
+        const ISoundTriggerHw::Properties *halProperties)
+{
+    strlcpy(properties->implementor,
+            halProperties->implementor.c_str(), SOUND_TRIGGER_MAX_STRING_LEN);
+    strlcpy(properties->description,
+            halProperties->description.c_str(), SOUND_TRIGGER_MAX_STRING_LEN);
+    properties->version = halProperties->version;
+    convertUuidFromHal(&properties->uuid, &halProperties->uuid);
+    properties->max_sound_models = halProperties->maxSoundModels;
+    properties->max_key_phrases = halProperties->maxKeyPhrases;
+    properties->max_users = halProperties->maxUsers;
+    properties->recognition_modes = halProperties->recognitionModes;
+    properties->capture_transition = (bool)halProperties->captureTransition;
+    properties->max_buffer_ms = halProperties->maxBufferMs;
+    properties->concurrent_capture = (bool)halProperties->concurrentCapture;
+    properties->trigger_in_event = (bool)halProperties->triggerInEvent;
+    properties->power_consumption_mw = halProperties->powerConsumptionMw;
+}
+
+void SoundTriggerHalHidl::convertTriggerPhraseToHal(
+        ISoundTriggerHw::Phrase *halTriggerPhrase,
+        const struct sound_trigger_phrase *triggerPhrase)
+{
+    halTriggerPhrase->id = triggerPhrase->id;
+    halTriggerPhrase->recognitionModes = triggerPhrase->recognition_mode;
+    halTriggerPhrase->users.setToExternal((uint32_t *)&triggerPhrase->users[0], triggerPhrase->num_users);
+    halTriggerPhrase->locale = triggerPhrase->locale;
+    halTriggerPhrase->text = triggerPhrase->text;
+}
+
+ISoundTriggerHw::SoundModel *SoundTriggerHalHidl::convertSoundModelToHal(
+        const struct sound_trigger_sound_model *soundModel)
+{
+    ISoundTriggerHw::SoundModel *halModel = NULL;
+    if (soundModel->type == SOUND_MODEL_TYPE_KEYPHRASE) {
+        ISoundTriggerHw::PhraseSoundModel *halKeyPhraseModel =
+                new ISoundTriggerHw::PhraseSoundModel();
+        struct sound_trigger_phrase_sound_model *keyPhraseModel =
+                (struct sound_trigger_phrase_sound_model *)soundModel;
+        ISoundTriggerHw::Phrase *halPhrases =
+                new ISoundTriggerHw::Phrase[keyPhraseModel->num_phrases];
+
+
+        for (unsigned int i = 0; i < keyPhraseModel->num_phrases; i++) {
+            convertTriggerPhraseToHal(&halPhrases[i],
+                                      &keyPhraseModel->phrases[i]);
+        }
+        halKeyPhraseModel->phrases.setToExternal(halPhrases, keyPhraseModel->num_phrases);
+        // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+        halKeyPhraseModel->phrases.resize(keyPhraseModel->num_phrases);
+
+        delete[] halPhrases;
+
+        halModel = (ISoundTriggerHw::SoundModel *)halKeyPhraseModel;
+    } else {
+        halModel = new ISoundTriggerHw::SoundModel();
+    }
+    halModel->type = (SoundModelType)soundModel->type;
+    convertUuidToHal(&halModel->uuid, &soundModel->uuid);
+    convertUuidToHal(&halModel->vendorUuid, &soundModel->vendor_uuid);
+    halModel->data.setToExternal((uint8_t *)soundModel + soundModel->data_offset, soundModel->data_size);
+    halModel->data.resize(soundModel->data_size);
+
+    return halModel;
+}
+
+void SoundTriggerHalHidl::convertPhraseRecognitionExtraToHal(
+        PhraseRecognitionExtra *halExtra,
+        const struct sound_trigger_phrase_recognition_extra *extra)
+{
+    halExtra->id = extra->id;
+    halExtra->recognitionModes = extra->recognition_modes;
+    halExtra->confidenceLevel = extra->confidence_level;
+    ConfidenceLevel *halLevels =
+            new ConfidenceLevel[extra->num_levels];
+    for (unsigned int i = 0; i < extra->num_levels; i++) {
+        halLevels[i].userId = extra->levels[i].user_id;
+        halLevels[i].levelPercent = extra->levels[i].level;
+    }
+    halExtra->levels.setToExternal(halLevels, extra->num_levels);
+    // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+    halExtra->levels.resize(extra->num_levels);
+
+    delete[] halLevels;
+}
+
+
+ISoundTriggerHw::RecognitionConfig *SoundTriggerHalHidl::convertRecognitionConfigToHal(
+        const struct sound_trigger_recognition_config *config)
+{
+    ISoundTriggerHw::RecognitionConfig *halConfig =
+            new ISoundTriggerHw::RecognitionConfig();
+
+    halConfig->captureHandle = config->capture_handle;
+    halConfig->captureDevice = (AudioDevice)config->capture_device;
+    halConfig->captureRequested = (uint32_t)config->capture_requested;
+
+    PhraseRecognitionExtra *halExtras =
+            new PhraseRecognitionExtra[config->num_phrases];
+
+    for (unsigned int i = 0; i < config->num_phrases; i++) {
+        convertPhraseRecognitionExtraToHal(&halExtras[i],
+                                  &config->phrases[i]);
+    }
+    halConfig->phrases.setToExternal(halExtras, config->num_phrases);
+    // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
+    halConfig->phrases.resize(config->num_phrases);
+
+    delete[] halExtras;
+
+    halConfig->data.setToExternal((uint8_t *)config + config->data_offset, config->data_size);
+
+    return halConfig;
+}
+
+
+// ISoundTriggerHwCallback
+::android::hardware::Return<void> SoundTriggerHalHidl::recognitionCallback(
+        const ISoundTriggerHwCallback::RecognitionEvent& halEvent,
+        CallbackCookie cookie)
+{
+    sp<SoundModel> model;
+    {
+        AutoMutex lock(mLock);
+        model = mSoundModels.valueFor((SoundModelHandle)cookie);
+        if (model == 0) {
+            return Return<void>();
+        }
+    }
+    struct sound_trigger_recognition_event *event = convertRecognitionEventFromHal(&halEvent);
+    if (event == NULL) {
+        return Return<void>();
+    }
+    event->model = model->mHandle;
+    model->mRecognitionCallback(event, model->mRecognitionCookie);
+
+    free(event);
+
+    return Return<void>();
+}
+
+::android::hardware::Return<void> SoundTriggerHalHidl::phraseRecognitionCallback(
+        const ISoundTriggerHwCallback::PhraseRecognitionEvent& halEvent,
+        CallbackCookie cookie)
+{
+    sp<SoundModel> model;
+    {
+        AutoMutex lock(mLock);
+        model = mSoundModels.valueFor((SoundModelHandle)cookie);
+        if (model == 0) {
+            return Return<void>();
+        }
+    }
+
+    struct sound_trigger_recognition_event *event = convertRecognitionEventFromHal(
+                                   (const ISoundTriggerHwCallback::RecognitionEvent *)&halEvent);
+    if (event == NULL) {
+        return Return<void>();
+    }
+
+    event->model = model->mHandle;
+    model->mRecognitionCallback(event, model->mRecognitionCookie);
+
+    free(event);
+
+    return Return<void>();
+}
+
+::android::hardware::Return<void> SoundTriggerHalHidl::soundModelCallback(
+        const ISoundTriggerHwCallback::ModelEvent& halEvent,
+        CallbackCookie cookie)
+{
+    sp<SoundModel> model;
+    {
+        AutoMutex lock(mLock);
+        model = mSoundModels.valueFor((SoundModelHandle)cookie);
+        if (model == 0) {
+            return Return<void>();
+        }
+    }
+
+    struct sound_trigger_model_event *event = convertSoundModelEventFromHal(&halEvent);
+    if (event == NULL) {
+        return Return<void>();
+    }
+
+    event->model = model->mHandle;
+    model->mSoundModelCallback(event, model->mSoundModelCookie);
+
+    free(event);
+
+    return Return<void>();
+}
+
+
+struct sound_trigger_model_event *SoundTriggerHalHidl::convertSoundModelEventFromHal(
+                                              const ISoundTriggerHwCallback::ModelEvent *halEvent)
+{
+    struct sound_trigger_model_event *event = (struct sound_trigger_model_event *)malloc(
+            sizeof(struct sound_trigger_model_event) +
+            halEvent->data.size());
+    if (event == NULL) {
+        return NULL;
+    }
+
+    event->status = (int)halEvent->status;
+    // event->model to be set by caller
+    event->data_offset = sizeof(struct sound_trigger_model_event);
+    event->data_size = halEvent->data.size();
+    uint8_t *dst = (uint8_t *)event + event->data_offset;
+    uint8_t *src = (uint8_t *)&halEvent->data[0];
+    memcpy(dst, src, halEvent->data.size());
+
+    return event;
+}
+
+void SoundTriggerHalHidl::convertPhraseRecognitionExtraFromHal(
+        struct sound_trigger_phrase_recognition_extra *extra,
+        const PhraseRecognitionExtra *halExtra)
+{
+    extra->id = halExtra->id;
+    extra->recognition_modes = halExtra->recognitionModes;
+    extra->confidence_level = halExtra->confidenceLevel;
+
+    size_t i;
+    for (i = 0; i < halExtra->levels.size() && i < SOUND_TRIGGER_MAX_USERS; i++) {
+        extra->levels[i].user_id = halExtra->levels[i].userId;
+        extra->levels[i].level = halExtra->levels[i].levelPercent;
+    }
+    extra->num_levels = (unsigned int)i;
+}
+
+
+struct sound_trigger_recognition_event *SoundTriggerHalHidl::convertRecognitionEventFromHal(
+        const ISoundTriggerHwCallback::RecognitionEvent *halEvent)
+{
+    struct sound_trigger_recognition_event *event;
+
+    if (halEvent->type == SoundModelType::KEYPHRASE) {
+        struct sound_trigger_phrase_recognition_event *phraseEvent =
+                (struct sound_trigger_phrase_recognition_event *)malloc(
+                        sizeof(struct sound_trigger_phrase_recognition_event) +
+                        halEvent->data.size());
+        if (phraseEvent == NULL) {
+            return NULL;
+        }
+        const ISoundTriggerHwCallback::PhraseRecognitionEvent *halPhraseEvent =
+                (const ISoundTriggerHwCallback::PhraseRecognitionEvent *)halEvent;
+
+        for (unsigned int i = 0; i < halPhraseEvent->phraseExtras.size(); i++) {
+            convertPhraseRecognitionExtraFromHal(&phraseEvent->phrase_extras[i],
+                                                 &halPhraseEvent->phraseExtras[i]);
+        }
+        phraseEvent->num_phrases = halPhraseEvent->phraseExtras.size();
+        event = (struct sound_trigger_recognition_event *)phraseEvent;
+        event->data_offset = sizeof(sound_trigger_phrase_recognition_event);
+    } else {
+        event = (struct sound_trigger_recognition_event *)malloc(
+                sizeof(struct sound_trigger_recognition_event) + halEvent->data.size());
+        if (event == NULL) {
+            return NULL;
+        }
+        event->data_offset = sizeof(sound_trigger_recognition_event);
+    }
+    event->status = (int)halEvent->status;
+    event->type = (sound_trigger_sound_model_type_t)halEvent->type;
+    // event->model to be set by caller
+    event->capture_available = (bool)halEvent->captureAvailable;
+    event->capture_session = halEvent->captureSession;
+    event->capture_delay_ms = halEvent->captureDelayMs;
+    event->capture_preamble_ms = halEvent->capturePreambleMs;
+    event->trigger_in_data = (bool)halEvent->triggerInData;
+    event->audio_config.sample_rate = halEvent->audioConfig.sampleRateHz;
+    event->audio_config.channel_mask = (audio_channel_mask_t)halEvent->audioConfig.channelMask;
+    event->audio_config.format = (audio_format_t)halEvent->audioConfig.format;
+
+    event->data_size = halEvent->data.size();
+    uint8_t *dst = (uint8_t *)event + event->data_offset;
+    uint8_t *src = (uint8_t *)&halEvent->data[0];
+    memcpy(dst, src, halEvent->data.size());
+
+    return event;
+}
+
+} // namespace android
diff --git a/services/soundtrigger/SoundTriggerHalHidl.h b/services/soundtrigger/SoundTriggerHalHidl.h
new file mode 100644
index 0000000..60404dc
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalHidl.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_HAL_HIDL_H
+#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_HIDL_H
+
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+#include "SoundTriggerHalInterface.h"
+#include <android/hardware/soundtrigger/2.0/types.h>
+#include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h>
+#include <android/hardware/soundtrigger/2.0/ISoundTriggerHwCallback.h>
+#include <android/hardware/soundtrigger/2.0/BnSoundTriggerHwCallback.h>
+
+namespace android {
+
+using android::hardware::audio::common::V2_0::Uuid;
+using android::hardware::soundtrigger::V2_0::ConfidenceLevel;
+using android::hardware::soundtrigger::V2_0::PhraseRecognitionExtra;
+using android::hardware::soundtrigger::V2_0::SoundModelType;
+using android::hardware::soundtrigger::V2_0::SoundModelHandle;
+using android::hardware::soundtrigger::V2_0::ISoundTriggerHw;
+using android::hardware::soundtrigger::V2_0::ISoundTriggerHwCallback;
+
+class SoundTriggerHalHidl : public SoundTriggerHalInterface,
+                            public virtual ISoundTriggerHwCallback
+
+{
+public:
+        virtual int getProperties(struct sound_trigger_properties *properties);
+
+        /*
+         * Load a sound model. Once loaded, recognition of this model can be started and stopped.
+         * Only one active recognition per model at a time. The SoundTrigger service will handle
+         * concurrent recognition requests by different users/applications on the same model.
+         * The implementation returns a unique handle used by other functions (unload_sound_model(),
+         * start_recognition(), etc...
+         */
+        virtual int loadSoundModel(struct sound_trigger_sound_model *sound_model,
+                                sound_model_callback_t callback,
+                                void *cookie,
+                                sound_model_handle_t *handle);
+
+        /*
+         * Unload a sound model. A sound model can be unloaded to make room for a new one to overcome
+         * implementation limitations.
+         */
+        virtual int unloadSoundModel(sound_model_handle_t handle);
+
+        /* Start recognition on a given model. Only one recognition active at a time per model.
+         * Once recognition succeeds of fails, the callback is called.
+         * TODO: group recognition configuration parameters into one struct and add key phrase options.
+         */
+        virtual int startRecognition(sound_model_handle_t handle,
+                                 const struct sound_trigger_recognition_config *config,
+                                 recognition_callback_t callback,
+                                 void *cookie);
+
+        /* Stop recognition on a given model.
+         * The implementation does not have to call the callback when stopped via this method.
+         */
+        virtual int stopRecognition(sound_model_handle_t handle);
+
+        /* Stop recognition on all models.
+         * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_1 or above.
+         * If no implementation is provided, stop_recognition will be called for each running model.
+         */
+        virtual int stopAllRecognitions();
+
+        // RefBase
+        virtual     void        onFirstRef();
+
+        // ISoundTriggerHwCallback
+        virtual ::android::hardware::Return<void> recognitionCallback(
+                const ISoundTriggerHwCallback::RecognitionEvent& event, CallbackCookie cookie);
+        virtual ::android::hardware::Return<void> phraseRecognitionCallback(
+                const ISoundTriggerHwCallback::PhraseRecognitionEvent& event, int32_t cookie);
+        virtual ::android::hardware::Return<void> soundModelCallback(
+                const ISoundTriggerHwCallback::ModelEvent& event, CallbackCookie cookie);
+private:
+        class SoundModel : public RefBase {
+        public:
+            SoundModel(sound_model_handle_t handle, sound_model_callback_t callback,
+                       void *cookie, android::hardware::soundtrigger::V2_0::SoundModelHandle halHandle)
+                 : mHandle(handle), mHalHandle(halHandle),
+                   mSoundModelCallback(callback), mSoundModelCookie(cookie),
+                   mRecognitionCallback(NULL), mRecognitionCookie(NULL) {}
+            ~SoundModel() {}
+
+            sound_model_handle_t   mHandle;
+            android::hardware::soundtrigger::V2_0::SoundModelHandle mHalHandle;
+            sound_model_callback_t mSoundModelCallback;
+            void *                 mSoundModelCookie;
+            recognition_callback_t mRecognitionCallback;
+            void *                 mRecognitionCookie;
+        };
+
+        friend class SoundTriggerHalInterface;
+
+        explicit SoundTriggerHalHidl(const char *moduleName = NULL);
+        virtual  ~SoundTriggerHalHidl();
+
+        void convertUuidToHal(Uuid *halUuid,
+                              const sound_trigger_uuid_t *uuid);
+        void convertUuidFromHal(sound_trigger_uuid_t *uuid,
+                                const Uuid *halUuid);
+
+        void convertPropertiesFromHal(
+                struct sound_trigger_properties *properties,
+                const ISoundTriggerHw::Properties *halProperties);
+
+        void convertTriggerPhraseToHal(
+                ISoundTriggerHw::Phrase *halTriggerPhrase,
+                const struct sound_trigger_phrase *triggerPhrase);
+        ISoundTriggerHw::SoundModel *convertSoundModelToHal(
+                const struct sound_trigger_sound_model *soundModel);
+
+        void convertPhraseRecognitionExtraToHal(
+                PhraseRecognitionExtra *halExtra,
+                const struct sound_trigger_phrase_recognition_extra *extra);
+        ISoundTriggerHw::RecognitionConfig *convertRecognitionConfigToHal(
+                const struct sound_trigger_recognition_config *config);
+
+        struct sound_trigger_model_event *convertSoundModelEventFromHal(
+                                              const ISoundTriggerHwCallback::ModelEvent *halEvent);
+        void convertPhraseRecognitionExtraFromHal(
+                struct sound_trigger_phrase_recognition_extra *extra,
+                const PhraseRecognitionExtra *halExtra);
+        struct sound_trigger_recognition_event *convertRecognitionEventFromHal(
+                const ISoundTriggerHwCallback::RecognitionEvent *halEvent);
+
+        uint32_t nextUniqueId();
+        sp<ISoundTriggerHw> getService();
+        void clearService();
+        sp<SoundModel> getModel(sound_model_handle_t handle);
+        sp<SoundModel> removeModel(sound_model_handle_t handle);
+
+        static pthread_once_t sOnceControl;
+        static void sOnceInit();
+
+        Mutex mLock;
+        Mutex mHalLock;
+        const char *mModuleName;
+        volatile atomic_uint_fast32_t  mNextUniqueId;
+        // Effect chains without a valid thread
+        DefaultKeyedVector< sound_model_handle_t , sp<SoundModel> > mSoundModels;
+        sp<::android::hardware::soundtrigger::V2_0::ISoundTriggerHw> mISoundTrigger;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_HIDL_H
diff --git a/services/soundtrigger/SoundTriggerHalInterface.h b/services/soundtrigger/SoundTriggerHalInterface.h
new file mode 100644
index 0000000..c083195
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalInterface.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_HAL_INTERFACE_H
+#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_INTERFACE_H
+
+#include <utils/RefBase.h>
+#include <system/sound_trigger.h>
+#include <hardware/sound_trigger.h>
+
+namespace android {
+
+class SoundTriggerHalInterface : public virtual RefBase
+{
+public:
+        /* get a sound trigger HAL instance */
+        static sp<SoundTriggerHalInterface> connectModule(const char *moduleName);
+
+        virtual     ~SoundTriggerHalInterface() {}
+
+        virtual int getProperties(struct sound_trigger_properties *properties) = 0;
+
+        /*
+         * Load a sound model. Once loaded, recognition of this model can be started and stopped.
+         * Only one active recognition per model at a time. The SoundTrigger service will handle
+         * concurrent recognition requests by different users/applications on the same model.
+         * The implementation returns a unique handle used by other functions (unload_sound_model(),
+         * start_recognition(), etc...
+         */
+        virtual int loadSoundModel(struct sound_trigger_sound_model *sound_model,
+                                sound_model_callback_t callback,
+                                void *cookie,
+                                sound_model_handle_t *handle) = 0;
+
+        /*
+         * Unload a sound model. A sound model can be unloaded to make room for a new one to overcome
+         * implementation limitations.
+         */
+        virtual int unloadSoundModel(sound_model_handle_t handle) = 0;
+
+        /* Start recognition on a given model. Only one recognition active at a time per model.
+         * Once recognition succeeds of fails, the callback is called.
+         * TODO: group recognition configuration parameters into one struct and add key phrase options.
+         */
+        virtual int startRecognition(sound_model_handle_t handle,
+                                 const struct sound_trigger_recognition_config *config,
+                                 recognition_callback_t callback,
+                                 void *cookie) = 0;
+
+        /* Stop recognition on a given model.
+         * The implementation does not have to call the callback when stopped via this method.
+         */
+        virtual int stopRecognition(sound_model_handle_t handle) = 0;
+
+        /* Stop recognition on all models.
+         * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_1 or above.
+         * If no implementation is provided, stop_recognition will be called for each running model.
+         */
+        virtual int stopAllRecognitions() = 0;
+
+protected:
+        SoundTriggerHalInterface() {}
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_INTERFACE_H
diff --git a/services/soundtrigger/SoundTriggerHalLegacy.cpp b/services/soundtrigger/SoundTriggerHalLegacy.cpp
new file mode 100644
index 0000000..2b78818
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalLegacy.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Log.h>
+#include "SoundTriggerHalLegacy.h"
+
+namespace android {
+
+/* static */
+sp<SoundTriggerHalInterface> SoundTriggerHalInterface::connectModule(const char *moduleName)
+{
+    return new SoundTriggerHalLegacy(moduleName);
+}
+
+SoundTriggerHalLegacy::SoundTriggerHalLegacy(const char *moduleName)
+    : mModuleName(moduleName), mHwDevice(NULL)
+{
+}
+
+void SoundTriggerHalLegacy::onFirstRef()
+{
+    const hw_module_t *mod;
+    int rc;
+
+    if (mModuleName == NULL) {
+        mModuleName = "primary";
+    }
+
+    rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, &mod);
+    if (rc != 0) {
+        ALOGE("couldn't load sound trigger module %s.%s (%s)",
+              SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, strerror(-rc));
+        return;
+    }
+    rc = sound_trigger_hw_device_open(mod, &mHwDevice);
+    if (rc != 0) {
+        ALOGE("couldn't open sound trigger hw device in %s.%s (%s)",
+              SOUND_TRIGGER_HARDWARE_MODULE_ID, mModuleName, strerror(-rc));
+        mHwDevice = NULL;
+        return;
+    }
+    if (mHwDevice->common.version < SOUND_TRIGGER_DEVICE_API_VERSION_1_0 ||
+            mHwDevice->common.version > SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) {
+        ALOGE("wrong sound trigger hw device version %04x", mHwDevice->common.version);
+        return;
+    }
+}
+
+SoundTriggerHalLegacy::~SoundTriggerHalLegacy()
+{
+    if (mHwDevice != NULL) {
+        sound_trigger_hw_device_close(mHwDevice);
+    }
+}
+
+int SoundTriggerHalLegacy::getProperties(struct sound_trigger_properties *properties)
+{
+    if (mHwDevice == NULL) {
+        return -ENODEV;
+    }
+    return mHwDevice->get_properties(mHwDevice, properties);
+}
+
+int SoundTriggerHalLegacy::loadSoundModel(struct sound_trigger_sound_model *sound_model,
+                        sound_model_callback_t callback,
+                        void *cookie,
+                        sound_model_handle_t *handle)
+{
+    if (mHwDevice == NULL) {
+        return -ENODEV;
+    }
+    return mHwDevice->load_sound_model(mHwDevice, sound_model, callback, cookie, handle);
+}
+
+int SoundTriggerHalLegacy::unloadSoundModel(sound_model_handle_t handle)
+{
+    if (mHwDevice == NULL) {
+        return -ENODEV;
+    }
+    return mHwDevice->unload_sound_model(mHwDevice, handle);
+}
+
+int SoundTriggerHalLegacy::startRecognition(sound_model_handle_t handle,
+                         const struct sound_trigger_recognition_config *config,
+                         recognition_callback_t callback,
+                         void *cookie)
+{
+    if (mHwDevice == NULL) {
+        return -ENODEV;
+    }
+    return mHwDevice->start_recognition(mHwDevice, handle, config, callback, cookie);
+}
+
+int SoundTriggerHalLegacy::stopRecognition(sound_model_handle_t handle)
+{
+    if (mHwDevice == NULL) {
+        return -ENODEV;
+    }
+    return mHwDevice->stop_recognition(mHwDevice, handle);
+}
+
+int SoundTriggerHalLegacy::stopAllRecognitions()
+{
+    if (mHwDevice == NULL) {
+        return -ENODEV;
+    }
+    if (mHwDevice->common.version >= SOUND_TRIGGER_DEVICE_API_VERSION_1_1 &&
+     mHwDevice->stop_all_recognitions) {
+        return mHwDevice->stop_all_recognitions(mHwDevice);
+    }
+    return -ENOSYS;
+}
+
+} // namespace android
diff --git a/services/soundtrigger/SoundTriggerHalLegacy.h b/services/soundtrigger/SoundTriggerHalLegacy.h
new file mode 100644
index 0000000..52488de
--- /dev/null
+++ b/services/soundtrigger/SoundTriggerHalLegacy.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_SOUNDTRIGGER_HAL_LEGACY_H
+#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_LEGACY_H
+
+#include "SoundTriggerHalInterface.h"
+
+namespace android {
+
+class SoundTriggerHalLegacy : public SoundTriggerHalInterface
+
+{
+public:
+        virtual             ~SoundTriggerHalLegacy();
+
+        virtual int getProperties(struct sound_trigger_properties *properties);
+
+        /*
+         * Load a sound model. Once loaded, recognition of this model can be started and stopped.
+         * Only one active recognition per model at a time. The SoundTrigger service will handle
+         * concurrent recognition requests by different users/applications on the same model.
+         * The implementation returns a unique handle used by other functions (unload_sound_model(),
+         * start_recognition(), etc...
+         */
+        virtual int loadSoundModel(struct sound_trigger_sound_model *sound_model,
+                                sound_model_callback_t callback,
+                                void *cookie,
+                                sound_model_handle_t *handle);
+
+        /*
+         * Unload a sound model. A sound model can be unloaded to make room for a new one to overcome
+         * implementation limitations.
+         */
+        virtual int unloadSoundModel(sound_model_handle_t handle);
+
+        /* Start recognition on a given model. Only one recognition active at a time per model.
+         * Once recognition succeeds of fails, the callback is called.
+         * TODO: group recognition configuration parameters into one struct and add key phrase options.
+         */
+        virtual int startRecognition(sound_model_handle_t handle,
+                                 const struct sound_trigger_recognition_config *config,
+                                 recognition_callback_t callback,
+                                 void *cookie);
+
+        /* Stop recognition on a given model.
+         * The implementation does not have to call the callback when stopped via this method.
+         */
+        virtual int stopRecognition(sound_model_handle_t handle);
+
+        /* Stop recognition on all models.
+         * Only supported for device api versions SOUND_TRIGGER_DEVICE_API_VERSION_1_1 or above.
+         * If no implementation is provided, stop_recognition will be called for each running model.
+         */
+        int stopAllRecognitions();
+
+        // RefBase
+        virtual     void        onFirstRef();
+
+private:
+
+        friend class SoundTriggerHalInterface;
+
+        explicit SoundTriggerHalLegacy(const char *moduleName = NULL);
+
+        const char *mModuleName;
+        struct sound_trigger_hw_device*        mHwDevice;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_LEGACY_H
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 6a52b9c..3ba7f62 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -32,17 +32,16 @@
 #include <binder/IServiceManager.h>
 #include <binder/MemoryBase.h>
 #include <binder/MemoryHeapBase.h>
-#include <hardware/sound_trigger.h>
+#include <system/sound_trigger.h>
 #include <ServiceUtilities.h>
 #include "SoundTriggerHwService.h"
 
-namespace android {
-
 #ifdef SOUND_TRIGGER_USE_STUB_MODULE
 #define HW_MODULE_PREFIX "stub"
 #else
 #define HW_MODULE_PREFIX "primary"
 #endif
+namespace android {
 
 SoundTriggerHwService::SoundTriggerHwService()
     : BnSoundTriggerHwService(),
@@ -54,30 +53,17 @@
 
 void SoundTriggerHwService::onFirstRef()
 {
-    const hw_module_t *mod;
     int rc;
-    sound_trigger_hw_device *dev;
 
-    rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, &mod);
-    if (rc != 0) {
-        ALOGE("couldn't load sound trigger module %s.%s (%s)",
-              SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, strerror(-rc));
-        return;
-    }
-    rc = sound_trigger_hw_device_open(mod, &dev);
-    if (rc != 0) {
-        ALOGE("couldn't open sound trigger hw device in %s.%s (%s)",
-              SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, strerror(-rc));
-        return;
-    }
-    if (dev->common.version < SOUND_TRIGGER_DEVICE_API_VERSION_1_0 ||
-        dev->common.version > SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) {
-        ALOGE("wrong sound trigger hw device version %04x", dev->common.version);
-        return;
-    }
+    sp<SoundTriggerHalInterface> halInterface =
+            SoundTriggerHalInterface::connectModule(HW_MODULE_PREFIX);
 
+    if (halInterface == 0) {
+        ALOGW("could not connect to HAL");
+        return;
+    }
     sound_trigger_module_descriptor descriptor;
-    rc = dev->get_properties(dev, &descriptor.properties);
+    rc = halInterface->getProperties(&descriptor.properties);
     if (rc != 0) {
         ALOGE("could not read implementation properties");
         return;
@@ -88,7 +74,7 @@
                                                  descriptor.handle);
 
     sp<ISoundTriggerClient> client;
-    sp<Module> module = new Module(this, dev, descriptor, client);
+    sp<Module> module = new Module(this, halInterface, descriptor, client);
     mModules.add(descriptor.handle, module);
     mCallbackThread = new CallbackThread(this);
 }
@@ -98,9 +84,6 @@
     if (mCallbackThread != 0) {
         mCallbackThread->exit();
     }
-    for (size_t i = 0; i < mModules.size(); i++) {
-        sound_trigger_hw_device_close(mModules.valueAt(i)->hwDevice());
-    }
 }
 
 status_t SoundTriggerHwService::listModules(struct sound_trigger_module_descriptor *modules,
@@ -489,10 +472,10 @@
 #define LOG_TAG "SoundTriggerHwService::Module"
 
 SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service,
-                                      sound_trigger_hw_device* hwDevice,
+                                      const sp<SoundTriggerHalInterface>& halInterface,
                                       sound_trigger_module_descriptor descriptor,
                                       const sp<ISoundTriggerClient>& client)
- : mService(service), mHwDevice(hwDevice), mDescriptor(descriptor),
+ : mService(service), mHalInterface(halInterface), mDescriptor(descriptor),
    mClient(client), mServiceState(SOUND_TRIGGER_STATE_NO_INIT)
 {
 }
@@ -510,10 +493,12 @@
         for (size_t i = 0; i < mModels.size(); i++) {
             sp<Model> model = mModels.valueAt(i);
             ALOGV("detach() unloading model %d", model->mHandle);
-            if (model->mState == Model::STATE_ACTIVE) {
-                mHwDevice->stop_recognition(mHwDevice, model->mHandle);
+            if (mHalInterface != 0) {
+                if (model->mState == Model::STATE_ACTIVE) {
+                    mHalInterface->stopRecognition(model->mHandle);
+                }
+                mHalInterface->unloadSoundModel(model->mHandle);
             }
-            mHwDevice->unload_sound_model(mHwDevice, model->mHandle);
         }
         mModels.clear();
     }
@@ -531,10 +516,12 @@
                                 sound_model_handle_t *handle)
 {
     ALOGV("loadSoundModel() handle");
+    if (mHalInterface == 0) {
+        return NO_INIT;
+    }
     if (!captureHotwordAllowed()) {
         return PERMISSION_DENIED;
     }
-
     if (modelMemory == 0 || modelMemory->pointer() == NULL) {
         ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()");
         return BAD_VALUE;
@@ -566,7 +553,7 @@
         return INVALID_OPERATION;
     }
 
-    status_t status = mHwDevice->load_sound_model(mHwDevice, sound_model,
+    status_t status = mHalInterface->loadSoundModel(sound_model,
                                                   SoundTriggerHwService::soundModelCallback,
                                                   this, handle);
 
@@ -601,6 +588,9 @@
 
 status_t SoundTriggerHwService::Module::unloadSoundModel_l(sound_model_handle_t handle)
 {
+    if (mHalInterface == 0) {
+        return NO_INIT;
+    }
     ssize_t index = mModels.indexOfKey(handle);
     if (index < 0) {
         return BAD_VALUE;
@@ -608,17 +598,20 @@
     sp<Model> model = mModels.valueAt(index);
     mModels.removeItem(handle);
     if (model->mState == Model::STATE_ACTIVE) {
-        mHwDevice->stop_recognition(mHwDevice, model->mHandle);
+        mHalInterface->stopRecognition(model->mHandle);
         model->mState = Model::STATE_IDLE;
     }
     AudioSystem::releaseSoundTriggerSession(model->mCaptureSession);
-    return mHwDevice->unload_sound_model(mHwDevice, handle);
+    return mHalInterface->unloadSoundModel(handle);
 }
 
 status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t handle,
                                  const sp<IMemory>& dataMemory)
 {
     ALOGV("startRecognition() model handle %d", handle);
+    if (mHalInterface == 0) {
+        return NO_INIT;
+    }
     if (!captureHotwordAllowed()) {
         return PERMISSION_DENIED;
     }
@@ -657,7 +650,7 @@
     //TODO: get capture handle and device from audio policy service
     config->capture_handle = model->mCaptureIOHandle;
     config->capture_device = model->mCaptureDevice;
-    status_t status = mHwDevice->start_recognition(mHwDevice, handle, config,
+    status_t status = mHalInterface->startRecognition(handle, config,
                                         SoundTriggerHwService::recognitionCallback,
                                         this);
 
@@ -672,6 +665,9 @@
 status_t SoundTriggerHwService::Module::stopRecognition(sound_model_handle_t handle)
 {
     ALOGV("stopRecognition() model handle %d", handle);
+    if (mHalInterface == 0) {
+        return NO_INIT;
+    }
     if (!captureHotwordAllowed()) {
         return PERMISSION_DENIED;
     }
@@ -685,7 +681,7 @@
     if (model->mState != Model::STATE_ACTIVE) {
         return INVALID_OPERATION;
     }
-    mHwDevice->stop_recognition(mHwDevice, handle);
+    mHalInterface->stopRecognition(handle);
     model->mState = Model::STATE_IDLE;
     return NO_ERROR;
 }
@@ -808,18 +804,13 @@
         }
 
         const bool supports_stop_all =
-            (mHwDevice->common.version >= SOUND_TRIGGER_DEVICE_API_VERSION_1_1 &&
-             mHwDevice->stop_all_recognitions);
-
-        if (supports_stop_all) {
-            mHwDevice->stop_all_recognitions(mHwDevice);
-        }
+                (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() == ENOSYS);
 
         for (size_t i = 0; i < mModels.size(); i++) {
             sp<Model> model = mModels.valueAt(i);
             if (model->mState == Model::STATE_ACTIVE) {
-                if (!supports_stop_all) {
-                    mHwDevice->stop_recognition(mHwDevice, model->mHandle);
+                if (mHalInterface != 0 && !supports_stop_all) {
+                    mHalInterface->stopRecognition(model->mHandle);
                 }
                 // keep model in ACTIVE state so that event is processed by onCallbackEvent()
                 if (model->mType == SOUND_MODEL_TYPE_KEYPHRASE) {
diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h
index 13a577a..7f7d0cc 100644
--- a/services/soundtrigger/SoundTriggerHwService.h
+++ b/services/soundtrigger/SoundTriggerHwService.h
@@ -26,7 +26,7 @@
 #include <soundtrigger/ISoundTrigger.h>
 #include <soundtrigger/ISoundTriggerClient.h>
 #include <system/sound_trigger.h>
-#include <hardware/sound_trigger.h>
+#include "SoundTriggerHalInterface.h"
 
 namespace android {
 
@@ -103,7 +103,7 @@
     public:
 
        Module(const sp<SoundTriggerHwService>& service,
-              sound_trigger_hw_device* hwDevice,
+              const sp<SoundTriggerHalInterface>& halInterface,
               sound_trigger_module_descriptor descriptor,
               const sp<ISoundTriggerClient>& client);
 
@@ -123,7 +123,6 @@
        virtual status_t dump(int fd, const Vector<String16>& args);
 
 
-       sound_trigger_hw_device *hwDevice() const { return mHwDevice; }
        struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
        void setClient(const sp<ISoundTriggerClient>& client) { mClient = client; }
        void clearClient() { mClient.clear(); }
@@ -146,7 +145,7 @@
 
         Mutex                                  mLock;
         wp<SoundTriggerHwService>              mService;
-        struct sound_trigger_hw_device*        mHwDevice;
+        sp<SoundTriggerHalInterface>           mHalInterface;
         struct sound_trigger_module_descriptor mDescriptor;
         sp<ISoundTriggerClient>                mClient;
         DefaultKeyedVector< sound_model_handle_t, sp<Model> >     mModels;
diff --git a/soundtrigger/Android.mk b/soundtrigger/Android.mk
index c794cc1..e29adbf 100644
--- a/soundtrigger/Android.mk
+++ b/soundtrigger/Android.mk
@@ -27,7 +27,6 @@
 	libutils \
 	liblog \
 	libbinder \
-	libhardware
 
 #LOCAL_C_INCLUDES += \
 	system/media/camera/include \