Merge "MediaCodec refactoring part 2-b: introduce BufferChannel"
diff --git a/camera/cameraserver/Android.mk b/camera/cameraserver/Android.mk
index 888862a..36e3927 100644
--- a/camera/cameraserver/Android.mk
+++ b/camera/cameraserver/Android.mk
@@ -22,7 +22,6 @@
 LOCAL_SHARED_LIBRARIES := \
 	libcameraservice \
 	liblog \
-	libcutils \
 	libutils \
 	libbinder \
 
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 0cba8b9..8f9333a 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -347,9 +347,7 @@
 
     sp<IStreamSource> source;
 
-    char prop[PROPERTY_VALUE_MAX];
-    bool usemp4 = property_get("media.stagefright.use-mp4source", prop, NULL) &&
-            (!strcmp(prop, "1") || !strcasecmp(prop, "true"));
+    bool usemp4 = property_get_bool("media.stagefright.use-mp4source", false);
 
     size_t len = strlen(argv[1]);
     if ((!usemp4 && len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) ||
diff --git a/include/media/AudioParameter.h b/include/media/AudioParameter.h
index 9719efa..1ace607 100644
--- a/include/media/AudioParameter.h
+++ b/include/media/AudioParameter.h
@@ -88,6 +88,7 @@
     status_t get(const String8& key, String8& value) const;
     status_t getInt(const String8& key, int& value) const;
     status_t getFloat(const String8& key, float& value) const;
+    status_t getAt(size_t index, String8& key) const;
     status_t getAt(size_t index, String8& key, String8& value) const;
 
     size_t size() const { return mParameters.size(); }
diff --git a/include/media/IMediaAnalyticsService.h b/include/media/IMediaAnalyticsService.h
new file mode 100644
index 0000000..21da6ad
--- /dev/null
+++ b/include/media/IMediaAnalyticsService.h
@@ -0,0 +1,76 @@
+/*
+ * 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_IMEDIAANALYTICSSERVICE_H
+#define ANDROID_IMEDIAANALYTICSSERVICE_H
+
+#include <utils/String8.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include <utils/List.h>
+
+#include <binder/IServiceManager.h>
+
+#include <media/MediaAnalyticsItem.h>
+// nope...#include <media/MediaAnalytics.h>
+
+namespace android {
+
+class IMediaAnalyticsService: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(MediaAnalyticsService);
+
+    // generate a unique sessionID to use across multiple requests
+    // 'unique' is within this device, since last reboot
+    virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() = 0;
+
+    // submit the indicated record to the mediaanalytics service, where
+    // it will be merged (if appropriate) with incomplete records that
+    // share the same key and sessionid.
+    // 'forcenew' marks any matching incomplete record as complete before
+    // inserting this new record.
+    // returns the sessionID associated with that item.
+    virtual MediaAnalyticsItem::SessionID_t submit(sp<MediaAnalyticsItem> item, bool forcenew) = 0;
+
+
+    // return lists of records that match the supplied parameters.
+    // finished [or not] records since time 'ts' with key 'key'
+    // timestamp 'ts' is nanoseconds, unix time.
+    virtual List<sp<MediaAnalyticsItem>> *getMediaAnalyticsItemList(bool finished, int64_t ts) = 0;
+    virtual List<sp<MediaAnalyticsItem>> *getMediaAnalyticsItemList(bool finished, int64_t ts, MediaAnalyticsItem::Key key) = 0;
+
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMediaAnalyticsService: public BnInterface<IMediaAnalyticsService>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IMEDIASTATISTICSSERVICE_H
diff --git a/include/media/MediaAnalyticsItem.h b/include/media/MediaAnalyticsItem.h
new file mode 100644
index 0000000..73c9dd4
--- /dev/null
+++ b/include/media/MediaAnalyticsItem.h
@@ -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.
+ */
+
+#ifndef ANDROID_MEDIA_MEDIAANALYTICSITEM_H
+#define ANDROID_MEDIA_MEDIAANALYTICSITEM_H
+
+#include <cutils/properties.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <media/stagefright/foundation/AString.h>
+
+namespace android {
+
+
+
+class IMediaAnalyticsService;
+
+// the class interface
+//
+
+class MediaAnalyticsItem : public RefBase {
+
+    friend class MediaAnalyticsService;
+    friend class IMediaAnalyticsService;
+
+    public:
+
+        // sessionid
+        // unique within device, within boot,
+        typedef int64_t SessionID_t;
+        static constexpr SessionID_t SessionIDInvalid = -1;
+        static constexpr SessionID_t SessionIDNone = 0;
+
+        // Key: the record descriminator
+        // values for the record discriminator
+        // values can be "component/component"
+        // basic values: "video", "audio", "drm"
+        // XXX: need to better define the format
+        typedef AString Key;
+        static const Key kKeyNone;              // ""
+        static const Key kKeyAny;               // "*"
+
+        // Attr: names for attributes within a record
+        // format "prop1" or "prop/subprop"
+        // XXX: need to better define the format
+        typedef AString Attr;
+
+
+    public:
+
+        // access functions for the class
+        MediaAnalyticsItem();
+        MediaAnalyticsItem(Key);
+        ~MediaAnalyticsItem();
+
+        // so clients can send intermediate values to be overlaid later
+        MediaAnalyticsItem &setFinalized(bool);
+        bool getFinalized() const;
+
+        // SessionID ties multiple submissions for same key together
+        // so that if video "height" and "width" are known at one point
+        // and "framerate" is only known later, they can be be brought
+        // together.
+        MediaAnalyticsItem &setSessionID(SessionID_t);
+        MediaAnalyticsItem &clearSessionID();
+        SessionID_t getSessionID() const;
+        // generates and stores a new ID iff mSessionID == SessionIDNone
+        SessionID_t generateSessionID();
+
+        // reset all contents, discarding any extra data
+        void clear();
+
+        // set the key discriminator for the record.
+        // most often initialized as part of the constructor
+        MediaAnalyticsItem &setKey(MediaAnalyticsItem::Key);
+        MediaAnalyticsItem::Key getKey();
+
+        // # of attributes in the record
+        int32_t count() const;
+
+        // set values appropriately
+        // return values tell us whether we overwrote an existing value
+        bool setInt32(Attr, int32_t value);
+        bool setInt64(Attr, int64_t value);
+        bool setDouble(Attr, double value);
+        bool setCString(Attr, const char *value);
+
+        // fused get/add/set; if attr wasn't there, it's a simple set.
+        // type-mismatch counts as "wasn't there".
+        // return value tells us whether we overwrote an existing value
+        bool addInt32(Attr, int32_t value);
+        bool addInt64(Attr, int64_t value);
+        bool addDouble(Attr, double value);
+
+        // find & extract values
+        // return indicates whether attr exists (and thus value filled in)
+        bool getInt32(Attr, int32_t *value);
+        bool getInt64(Attr, int64_t *value);
+        bool getDouble(Attr, double *value);
+        bool getCString(Attr, char **value);
+
+        // parameter indicates whether to close any existing open
+        // record with same key before establishing a new record
+        bool selfrecord(bool);
+        bool selfrecord();
+
+        // remove indicated attributes and their values
+        // filterNot() could also be called keepOnly()
+        // return value is # attributes removed
+        // XXX: perhaps 'remove' instead of 'filter'
+        // XXX: filterNot would become 'keep'
+        int32_t filter(int count, Attr attrs[]);
+        int32_t filterNot(int count, Attr attrs[]);
+        int32_t filter(Attr attr);
+
+        // below here are used on server side or to talk to server
+        // clients need not worry about these.
+
+        // timestamp, pid, and uid only used on server side
+	// timestamp is in 'nanoseconds, unix time'
+        MediaAnalyticsItem &setTimestamp(nsecs_t);
+        nsecs_t getTimestamp() const;
+
+        MediaAnalyticsItem &setPid(pid_t);
+        pid_t getPid() const;
+
+        MediaAnalyticsItem &setUid(uid_t);
+        uid_t getUid() const;
+
+        // our serialization code for binder calls
+        int32_t writeToParcel(Parcel *);
+        int32_t readFromParcel(const Parcel&);
+
+        AString toString();
+
+        // are we collecting analytics data
+        static bool isEnabled();
+
+    protected:
+
+        // merge fields from arg into this
+        // with rules for first/last/add, etc
+        // XXX: document semantics and how they are indicated
+        bool merge(sp<MediaAnalyticsItem> );
+
+        // enabled 1, disabled 0
+        static const char * const EnabledProperty;
+        static const char * const EnabledPropertyPersist;
+        static const int   EnabledProperty_default;
+
+    private:
+
+        // to help validate that A doesn't mess with B's records
+        pid_t     mPid;
+        uid_t     mUid;
+
+        // let's reuse a binder connection
+        static sp<IMediaAnalyticsService> sAnalyticsService;
+        static sp<IMediaAnalyticsService> getInstance();
+
+        // tracking information
+        SessionID_t mSessionID;         // grouping similar records
+        nsecs_t mTimestamp;             // ns, system_time_monotonic
+
+        // will this record accept further updates
+        bool mFinalized;
+
+        Key mKey;
+
+        class Item : public RefBase {
+
+         public:
+
+            enum Type {
+                kTypeNone = 0,
+                kTypeInt32 = 1,
+                kTypeInt64 = 2,
+                kTypeDouble = 3,
+                kTypeCString = 4,
+            };
+
+            Item();
+            ~Item();
+            void clear();
+
+            Type mType;
+            union {
+                    int32_t int32Value;
+                    int64_t int64Value;
+                    double doubleValue;
+                    char *CStringValue;
+            } u;
+        };
+        KeyedVector<Attr, sp<Item>> mItems;
+
+};
+
+} // namespace android
+
+#endif
diff --git a/include/media/audiohal/DevicesFactoryHalInterface.h b/include/media/audiohal/DevicesFactoryHalInterface.h
index 823a0da..14af384 100644
--- a/include/media/audiohal/DevicesFactoryHalInterface.h
+++ b/include/media/audiohal/DevicesFactoryHalInterface.h
@@ -26,8 +26,6 @@
 class DevicesFactoryHalInterface : public RefBase
 {
   public:
-    virtual ~DevicesFactoryHalInterface() {}
-
     // Opens a device with the specified name. To close the device, it is
     // necessary to release references to the returned object.
     virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device) = 0;
@@ -37,6 +35,8 @@
   protected:
     // Subclasses can not be constructed directly by clients.
     DevicesFactoryHalInterface() {}
+
+    virtual ~DevicesFactoryHalInterface() {}
 };
 
 } // namespace android
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 0c93ca5..cde188c 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -171,6 +171,7 @@
         sp<RefBase> mCodecRef;            // and a reference to the IMemory
 
         sp<GraphicBuffer> mGraphicBuffer;
+        bool mNewGraphicBuffer;
         int mFenceFd;
         FrameRenderTracker::Info *mRenderInfo;
 
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 3479f76..a8dcbe0 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -44,6 +44,7 @@
         kStreamedFromLocalHost = 2,
         kIsCachingDataSource   = 4,
         kIsHTTPBasedSource     = 8,
+        kIsLocalFileSource     = 16,
     };
 
     static sp<DataSource> CreateFromURI(
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
index b6349e0..9f3bb5e 100644
--- a/include/media/stagefright/FileSource.h
+++ b/include/media/stagefright/FileSource.h
@@ -39,6 +39,10 @@
 
     virtual status_t getSize(off64_t *size);
 
+    virtual uint32_t flags() {
+        return kIsLocalFileSource;
+    }
+
     virtual sp<DecryptHandle> DrmInitialization(const char *mime);
 
     virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
diff --git a/include/media/stagefright/foundation/AData.h b/include/media/stagefright/foundation/AData.h
new file mode 100644
index 0000000..49aa0dc
--- /dev/null
+++ b/include/media/stagefright/foundation/AData.h
@@ -0,0 +1,843 @@
+/*
+ * 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 STAGEFRIGHT_FOUNDATION_A_DATA_H_
+#define STAGEFRIGHT_FOUNDATION_A_DATA_H_
+
+#include <memory> // for std::shared_ptr, weak_ptr and unique_ptr
+#include <type_traits> // for std::aligned_union
+
+#include <utils/StrongPointer.h> // for android::sp and wp
+
+#include <media/stagefright/foundation/TypeTraits.h>
+#include <media/stagefright/foundation/Flagged.h>
+
+namespace android {
+
+/**
+ * AData is a flexible union type that supports non-POD members. It supports arbitrary types as long
+ * as they are either moveable or copyable.
+ *
+ * Internally, AData is using AUnion - a structure providing the union support. AUnion should not
+ * be used by generic code as it is very unsafe - it opens type aliasing errors where an object of
+ * one type can be easily accessed as an object of another type. AData prevents this.
+ *
+ * AData allows a custom type flagger to be used for future extensions (e.g. allowing automatic
+ * type conversion). A strict and a relaxed flagger are provided as internal types.
+ *
+ * Use as follows:
+ *
+ * AData<int, float>::Basic data; // strict type support
+ * int i = 1;
+ * float f = 7.0f;
+ *
+ * data.set(5);
+ * EXPECT_TRUE(data.find(&i));
+ * EXPECT_FALSE(data.find(&f));
+ * EXPECT_EQ(i, 5);
+ *
+ * data.set(6.0f);
+ * EXPECT_FALSE(data.find(&i));
+ * EXPECT_TRUE(data.find(&f));
+ * EXPECT_EQ(f, 6.0f);
+ *
+ * AData<int, sp<RefBase>>::RelaxedBasic objdata; // relaxed type support
+ * sp<ABuffer> buf = new ABuffer(16), buf2;
+ * sp<RefBase> obj;
+ *
+ * objdata.set(buf);
+ * EXPECT_TRUE(objdata.find(&buf2));
+ * EXPECT_EQ(buf, buf2);
+ * EXPECT_FALSE(objdata.find(&i));
+ * EXPECT_TRUE(objdata.find(&obj));
+ * EXPECT_TRUE(obj == buf);
+ *
+ * obj = buf;
+ * objdata.set(obj); // storing as sp<RefBase>
+ * EXPECT_FALSE(objdata.find(&buf2));  // not stored as ABuffer(!)
+ * EXPECT_TRUE(objdata.find(&obj));
+ */
+
+/// \cond Internal
+
+/**
+ * Helper class to call constructor and destructor for a specific type in AUnion.
+ * This class is needed as member function specialization is not allowed for a
+ * templated class.
+ */
+struct _AUnion_impl {
+    /**
+     * Calls placement constuctor for type T with arbitrary arguments for a storage at an address.
+     * Storage MUST be large enough to contain T.
+     * Also clears the slack space after type T. \todo This is not technically needed, so we may
+     * choose to do this just for debugging.
+     *
+     * \param totalSize size of the storage
+     * \param addr      pointer to where object T should be constructed
+     * \param args      arbitrary arguments for constructor
+     */
+    template<typename T, typename ...Args>
+    inline static void emplace(size_t totalSize, T *addr, Args&&... args) {
+        new(addr)T(std::forward<Args>(args)...);
+        // clear slack space - this is not technically required
+        constexpr size_t size = sizeof(T);
+        memset(reinterpret_cast<uint8_t*>(addr) + size, 0, totalSize - size);
+    }
+
+    /**
+     * Calls destuctor for an object of type T located at a specific address.
+     *
+     * \note we do not clear the storage in this case as the storage should not be used
+     * until another object is placed there, at which case the storage will be cleared.
+     *
+     * \param addr    pointer to where object T is stored
+     */
+    template<typename T>
+    inline static void del(T *addr) {
+        addr->~T();
+    }
+};
+
+/** Constructor specialization for void type */
+template<>
+inline void _AUnion_impl::emplace<void>(size_t totalSize, void *addr) {
+    memset(addr, 0, totalSize);
+}
+
+/** Destructor specialization for void type */
+template<>
+inline void _AUnion_impl::del<void>(void *) {
+}
+
+/// \endcond
+
+/**
+ * A templated union class that can contain specific types of data, and provides
+ * constructors, destructor and access methods strictly for those types.
+ *
+ * \note This class is VERY UNSAFE compared to a union, but it allows non-POD unions.
+ * In particular care must be taken that methods are called in a careful order to
+ * prevent accessing objects of one type as another type. This class provides no
+ * facilities to help with this ordering. This is meant to be wrapped by safer
+ * utility classes that do that.
+ *
+ * \param Ts types stored in this union.
+ */
+template<typename ...Ts>
+struct AUnion {
+private:
+    using _type = typename std::aligned_union<0, Ts...>::type; ///< storage type
+    _type mValue;                                              ///< storage
+
+public:
+    /**
+     * Constructs an object of type T with arbitrary arguments in this union. After this call,
+     * this union will contain this object.
+     *
+     * This method MUST be called only when either 1) no object or 2) a void object (equivalent to
+     * no object) is contained in this union.
+     *
+     * \param T     type of object to be constructed. This must be one of the template parameters of
+     *              the union class with the same cv-qualification, or void.
+     * \param args  arbitrary arguments for the constructor
+     */
+    template<
+            typename T, typename ...Args,
+            typename=typename std::enable_if<is_one_of<T, void, Ts...>::value>::type>
+    inline void emplace(Args&&... args) {
+        _AUnion_impl::emplace(
+                sizeof(_type), reinterpret_cast<T*>(&mValue), std::forward<Args>(args)...);
+    }
+
+    /**
+     * Destructs an object of type T in this union. After this call, this union will contain no
+     * object.
+     *
+     * This method MUST be called only when this union contains an object of type T.
+     *
+     * \param T     type of object to be destructed. This must be one of the template parameters of
+     *              the union class with the same cv-qualification, or void.
+     */
+    template<
+            typename T,
+            typename=typename std::enable_if<is_one_of<T, void, Ts...>::value>::type>
+    inline void del() {
+        _AUnion_impl::del(reinterpret_cast<T*>(&mValue));
+    }
+
+    /**
+     * Returns a const reference to the object of type T in this union.
+     *
+     * This method MUST be called only when this union contains an object of type T.
+     *
+     * \param T     type of object to be returned. This must be one of the template parameters of
+     *              the union class with the same cv-qualification.
+     */
+    template<
+            typename T,
+            typename=typename std::enable_if<is_one_of<T, Ts...>::value>::type>
+    inline const T &get() const {
+        return *reinterpret_cast<const T*>(&mValue);
+    }
+
+    /**
+     * Returns a reference to the object of type T in this union.
+     *
+     * This method MUST be called only when this union contains an object of type T.
+     *
+     * \param T     type of object to be returned. This must be one of the template parameters of
+     *              the union class with the same cv-qualification.
+     */
+    template<typename T>
+    inline T &get() {
+        return *reinterpret_cast<T*>(&mValue);
+    }
+};
+
+/**
+ * Helper utility class that copies an object of type T to a destination.
+ *
+ * T must be copy assignable or copy constructible.
+ *
+ * It provides:
+ *
+ * void assign(T*, const U&) // for copiable types - this leaves the source unchanged, hence const.
+ *
+ * \param T type of object to assign to
+ */
+template<
+        typename T,
+        bool=std::is_copy_assignable<T>::value>
+struct _AData_copier {
+    static_assert(std::is_copy_assignable<T>::value, "T must be copy assignable here");
+
+    /**
+     * Copies src to data without modifying data.
+     *
+     * \param data pointer to destination
+     * \param src source object
+     */
+    inline static void assign(T *data, const T &src) {
+        *data = src;
+    }
+
+    template<typename U>
+    using enable_if_T_is_same_as = typename std::enable_if<std::is_same<U, T>::value>::type;
+
+    /**
+     * Downcast specializations for sp<>, shared_ptr<> and weak_ptr<>
+     */
+    template<typename Tp, typename U, typename=enable_if_T_is_same_as<sp<Tp>>>
+    inline static void assign(sp<Tp> *data, const sp<U> &src) {
+        *data = static_cast<Tp*>(src.get());
+    }
+
+    template<typename Tp, typename U, typename=enable_if_T_is_same_as<wp<Tp>>>
+    inline static void assign(wp<Tp> *data, const wp<U> &src) {
+        sp<U> __tmp = src.promote();
+        *data = static_cast<Tp*>(__tmp.get());
+    }
+
+    template<typename Tp, typename U, typename=enable_if_T_is_same_as<sp<Tp>>>
+    inline static void assign(sp<Tp> *data, sp<U> &&src) {
+        sp<U> __tmp = std::move(src); // move src out as get cannot
+        *data = static_cast<Tp*>(__tmp.get());
+    }
+
+    template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::shared_ptr<Tp>>>
+    inline static void assign(std::shared_ptr<Tp> *data, const std::shared_ptr<U> &src) {
+        *data = std::static_pointer_cast<Tp>(src);
+    }
+
+    template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::shared_ptr<Tp>>>
+    inline static void assign(std::shared_ptr<Tp> *data, std::shared_ptr<U> &&src) {
+        std::shared_ptr<U> __tmp = std::move(src); // move src out as static_pointer_cast cannot
+        *data = std::static_pointer_cast<Tp>(__tmp);
+    }
+
+    template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::weak_ptr<Tp>>>
+    inline static void assign(std::weak_ptr<Tp> *data, const std::weak_ptr<U> &src) {
+        *data = std::static_pointer_cast<Tp>(src.lock());
+    }
+
+    // shared_ptrs are implicitly convertible to weak_ptrs but not vice versa, but picking the
+    // first compatible type in Ts requires having shared_ptr types before weak_ptr types, so that
+    // they are stored as shared_ptrs.
+    /**
+     * Provide sensible error message if encountering shared_ptr/weak_ptr ambiguity. This method
+     * is not enough to detect this, only if someone is trying to find the shared_ptr.
+     */
+    template<typename Tp, typename U>
+    inline static void assign(std::shared_ptr<Tp> *, const std::weak_ptr<U> &) {
+        static_assert(std::is_same<Tp, void>::value,
+                      "shared/weak pointer ambiguity. move shared ptr types before weak_ptrs");
+    }
+};
+
+/**
+ * Template specialization for non copy assignable, but copy constructible types.
+ *
+ * \todo Test this. No basic classes are copy constructible but not assignable.
+ *
+ */
+template<typename T>
+struct _AData_copier<T, false> {
+    static_assert(!std::is_copy_assignable<T>::value, "T must not be copy assignable here");
+    static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible here");
+
+    inline static void copy(T *data, const T &src) {
+        data->~T();
+        new(data)T(src);
+    }
+};
+
+/**
+ * Helper utility class that moves an object of type T to a destination.
+ *
+ * T must be move assignable or move constructible.
+ *
+ * It provides multiple methods:
+ *
+ * void assign(T*, T&&)
+ *
+ * \param T type of object to assign
+ */
+template<
+        typename T,
+        bool=std::is_move_assignable<T>::value>
+struct _AData_mover {
+    static_assert(std::is_move_assignable<T>::value, "T must be move assignable here");
+
+    /**
+     * Moves src to data while likely modifying it.
+     *
+     * \param data pointer to destination
+     * \param src source object
+     */
+    inline static void assign(T *data, T &&src) {
+        *data = std::move(src);
+    }
+
+    template<typename U>
+    using enable_if_T_is_same_as = typename std::enable_if<std::is_same<U, T>::value>::type;
+
+    /**
+     * Downcast specializations for sp<>, shared_ptr<> and weak_ptr<>
+     */
+    template<typename Tp, typename U, typename=enable_if_T_is_same_as<sp<Tp>>>
+    inline static void assign(sp<Tp> *data, sp<U> &&src) {
+        sp<U> __tmp = std::move(src); // move src out as get cannot
+        *data = static_cast<Tp*>(__tmp.get());
+    }
+
+    template<typename Tp, typename U, typename=enable_if_T_is_same_as<std::shared_ptr<Tp>>>
+    inline static void assign(std::shared_ptr<Tp> *data, std::shared_ptr<U> &&src) {
+        std::shared_ptr<U> __tmp = std::move(src); // move src out as static_pointer_cast cannot
+        *data = std::static_pointer_cast<Tp>(__tmp);
+    }
+
+    template<typename Tp, typename Td, typename U, typename Ud,
+            typename=enable_if_T_is_same_as<std::unique_ptr<Tp, Td>>>
+    inline static void assign(std::unique_ptr<Tp, Td> *data, std::unique_ptr<U, Ud> &&src) {
+        *data = std::unique_ptr<Tp, Td>(static_cast<Tp*>(src.release()));
+    }
+
+    // shared_ptrs are implicitly convertible to weak_ptrs but not vice versa, but picking the
+    // first compatible type in Ts requires having shared_ptr types before weak_ptr types, so that
+    // they are stored as shared_ptrs.
+    /**
+     * Provide sensible error message if encountering shared_ptr/weak_ptr ambiguity. This method
+     * is not enough to detect this, only if someone is trying to remove the shared_ptr.
+     */
+    template<typename Tp, typename U>
+    inline static void assign(std::shared_ptr<Tp> *, std::weak_ptr<U> &&) {
+        static_assert(std::is_same<Tp, void>::value,
+                      "shared/weak pointer ambiguity. move shared ptr types before weak_ptrs");
+    }
+
+    // unique_ptrs are implicitly convertible to shared_ptrs but not vice versa, but picking the
+    // first compatible type in Ts requires having unique_ptrs types before shared_ptrs types, so
+    // that they are stored as unique_ptrs.
+    /**
+     * Provide sensible error message if encountering shared_ptr/unique_ptr ambiguity. This method
+     * is not enough to detect this, only if someone is trying to remove the unique_ptr.
+     */
+    template<typename Tp, typename U>
+    inline static void assign(std::unique_ptr<Tp> *, std::shared_ptr<U> &&) {
+        static_assert(std::is_same<Tp, void>::value,
+                      "unique/shared pointer ambiguity. move unique ptr types before shared_ptrs");
+    }
+};
+
+/**
+ * Template specialization for non move assignable, but move constructible types.
+ *
+ * \todo Test this. No basic classes are move constructible but not assignable.
+ *
+ */
+template<typename T>
+struct _AData_mover<T, false> {
+    static_assert(!std::is_move_assignable<T>::value, "T must not be move assignable here");
+    static_assert(std::is_move_constructible<T>::value, "T must be move constructible here");
+
+    inline static void assign(T *data, T &&src) {
+        data->~T();
+        new(data)T(std::move(src));
+    }
+};
+
+/**
+ * Helper template that deletes an object of a specific type (member) in an AUnion.
+ *
+ * \param Flagger type flagger class (see AData)
+ * \param U AUnion object in which the member should be deleted
+ * \param Ts types to consider for the member
+ */
+template<typename Flagger, typename U, typename ...Ts>
+struct _AData_deleter;
+
+/**
+ * Template specialization when there are still types to consider (T and rest)
+ */
+template<typename Flagger, typename U, typename T, typename ...Ts>
+struct _AData_deleter<Flagger, U, T, Ts...> {
+    static bool del(typename Flagger::type flags, U &data) {
+        if (Flagger::canDeleteAs(flags, Flagger::flagFor((T*)0))) {
+            data.template del<T>();
+            return true;
+        }
+        return _AData_deleter<Flagger, U, Ts...>::del(flags, data);
+    }
+};
+
+/**
+ * Template specialization when there are no more types to consider.
+ */
+template<typename Flagger, typename U>
+struct _AData_deleter<Flagger, U> {
+    inline static bool del(typename Flagger::type, U &) {
+        return false;
+    }
+};
+
+/**
+ * Container that can store an arbitrary object of a set of specified types.
+ *
+ * This struct is an outer class that contains various inner classes based on desired type
+ * strictness. The following inner classes are supported:
+ *
+ * AData<types...>::Basic   - strict type support using uint32_t flag.
+ *
+ * AData<types...>::Strict<Flag> - strict type support using custom flag.
+ * AData<types...>::Relaxed<Flag, MaxSize, Align>
+ *                          - relaxed type support with compatible (usually derived) class support
+ *                            for pointer types with added size checking for minimal additional
+ *                            safety.
+ *
+ * AData<types...>::RelaxedBasic - strict type support using uint32_t flag.
+ *
+ * AData<types...>::Custom<flagger> - custom type support (flaggers determine the supported types
+ *                                    and the base type to use for each)
+ *
+ */
+template<typename ...Ts>
+struct AData {
+private:
+    static_assert(are_unique<Ts...>::value, "types must be unique");
+
+    static constexpr size_t num_types = sizeof...(Ts); ///< number of types to support
+
+public:
+    /**
+     * Default (strict) type flagger provided.
+     *
+     * The default flagger simply returns the index of the type within Ts, or 0 for void.
+     *
+     * Type flaggers return a flag for a supported type.
+     *
+     * They must provide:
+     *
+     * - a flagFor(T*) method for supported types _and_ for T=void. T=void is used to mark that no
+     *   object is stored in the container. For this, an arbitrary unique value may be returned.
+     * - a mask field that contains the flag mask.
+     * - a canDeleteAs(Flag, Flag) flag comparison method that checks if a type of a flag can be
+     *   deleted as another type.
+     *
+     * \param Flag the underlying unsigned integral to use for the flags.
+     */
+    template<typename Flag>
+    struct flagger {
+    private:
+        static_assert(std::is_unsigned<Flag>::value, "Flag must be unsigned");
+        static_assert(std::is_integral<Flag>::value, "Flag must be an integral type");
+
+        static constexpr Flag count = num_types + 1;
+
+    public:
+        typedef Flag type; ///< flag type
+
+        static constexpr Flag mask = _Flagged_helper::minMask<Flag>(count); ///< flag mask
+
+        /**
+         * Return the stored type for T. This is itself.
+         */
+        template<typename T>
+        struct store {
+            typedef T as_type; ///< the base type that T is stored as
+        };
+
+        /**
+         * Constexpr method that returns if two flags are compatible for deletion.
+         *
+         * \param objectFlag flag for object to be deleted
+         * \param deleteFlag flag for type that object is to be deleted as
+         */
+        static constexpr bool canDeleteAs(Flag objectFlag, Flag deleteFlag) {
+            // default flagger requires strict type equality
+            return objectFlag == deleteFlag;
+        }
+
+        /**
+         * Constexpr method that returns the flag to use for a given type.
+         *
+         * Function overload for void*.
+         */
+        static constexpr Flag flagFor(void*) {
+            return 0u;
+        }
+
+        /**
+         * Constexpr method that returns the flag to use for a given supported type (T).
+         */
+        template<typename T, typename=typename std::enable_if<is_one_of<T, Ts...>::value>::type>
+        static constexpr Flag flagFor(T*) {
+            return find_first<T, Ts...>::index;
+        }
+    };
+
+    /**
+     * Relaxed flagger returns the index of the type within Ts. However, for pointers T* it returns
+     * the first type in Ts that T* can be converted into (this is normally a base type, but also
+     * works for sp<>, shared_ptr<> or unique_ptr<>). For a bit more strictness, the flag also
+     * contains the size of the class to avoid finding objects that were stored as a different
+     * derived class of the same base class.
+     *
+     * Flag is basically the index of the (base) type in Ts multiplied by the max size stored plus
+     * the size of the type (divided by alignment) for derived pointer types.
+     *
+     * \param MaxSize max supported size for derived class pointers
+     * \param Align alignment to assume for derived class pointers
+     */
+    template<typename Flag, size_t MaxSize=1024, size_t Align=4>
+    struct relaxed_flagger {
+    private:
+        static_assert(std::is_unsigned<Flag>::value, "Flag must be unsigned");
+        static_assert(std::is_integral<Flag>::value, "Flag must be an integral type");
+
+        static constexpr Flag count = num_types + 1;
+        static_assert(std::numeric_limits<Flag>::max() / count > (MaxSize / Align),
+                      "not enough bits to fit into flag");
+
+        static constexpr Flag max_size_stored = MaxSize / Align + 1;
+
+        // T can be converted if it's size is <= MaxSize and it can be converted to one of the Ts
+        template<typename T, size_t size>
+        using enable_if_can_be_converted = typename std::enable_if<
+                (size / Align < max_size_stored
+                        && find_first_convertible_to<T, Ts...>::index)>::type;
+
+
+        template<typename W, typename T, typename=enable_if_can_be_converted<W, sizeof(T)>>
+        static constexpr Flag relaxedFlagFor(W*, T*) {
+            return find_first_convertible_to<W, Ts...>::index * max_size_stored
+                    + (is_one_of<W, Ts...>::value ? 0 : (sizeof(T) / Align));
+        }
+
+    public:
+        typedef Flag type; ///< flag type
+
+        static constexpr Flag mask =
+            _Flagged_helper::minMask<Flag>(count * max_size_stored); ///< flag mask
+
+        /**
+         * Constexpr method that returns if two flags are compatible for deletion.
+         *
+         * \param objectFlag flag for object to be deleted
+         * \param deleteFlag flag for type that object is to be deleted as
+         */
+        static constexpr bool canDeleteAs(Flag objectFlag, Flag deleteFlag) {
+            // can delete if objects have the same base type
+            return
+                objectFlag / max_size_stored == deleteFlag / max_size_stored &&
+                (deleteFlag % max_size_stored) == 0;
+        }
+
+        /**
+         * Constexpr method that returns the flag to use for a given type.
+         *
+         * Function overload for void*.
+         */
+        static constexpr Flag flagFor(void*) {
+            return 0u;
+        }
+
+        /**
+         * Constexpr method that returns the flag to use for a given supported type (T).
+         *
+         * This is a member method to enable both overloading as well as template specialization.
+         */
+        template<typename T, typename=typename std::enable_if<is_one_of<T, Ts...>::value>::type>
+        static constexpr Flag flagFor(T*) {
+            return find_first<T, Ts...>::index * max_size_stored;
+        }
+
+        /**
+         * For precaution, we only consider converting pointers to their base classes.
+         */
+
+        /**
+         * Template specialization for derived class pointers and managed pointers.
+         */
+        template<typename T>
+        static constexpr Flag flagFor(T**p) { return relaxedFlagFor(p, (T*)0); }
+        template<typename T>
+        static constexpr Flag flagFor(std::shared_ptr<T>*p) { return relaxedFlagFor(p, (T*)0); }
+        template<typename T>
+        static constexpr Flag flagFor(std::unique_ptr<T>*p) { return relaxedFlagFor(p, (T*)0); }
+        template<typename T>
+        static constexpr Flag flagFor(std::weak_ptr<T>*p) { return relaxedFlagFor(p, (T*)0); }
+        template<typename T>
+        static constexpr Flag flagFor(sp<T>*p) { return relaxedFlagFor(p, (T*)0); }
+        template<typename T>
+        static constexpr Flag flagFor(wp<T>*p) { return relaxedFlagFor(p, (T*)0); }
+
+        /**
+         * Type support template that provodes the stored type for T.
+         * This is itself if it is one of Ts, or the first type in Ts that T is convertible to.
+         *
+         * NOTE: This template may provide a base class for an unsupported type. Support is
+         * determined by flagFor().
+         */
+        template<typename T>
+        struct store {
+            typedef typename std::conditional<
+                    is_one_of<T, Ts...>::value,
+                    T,
+                    typename find_first_convertible_to<T, Ts...>::type>::type as_type;
+        };
+    };
+
+    /**
+     * Implementation of AData.
+     */
+    template<typename Flagger>
+    struct Custom : protected Flagged<AUnion<Ts...>, typename Flagger::type, Flagger::mask> {
+        using data_t = AUnion<Ts...>;
+        using base_t = Flagged<AUnion<Ts...>, typename Flagger::type, Flagger::mask>;
+
+        /**
+         * Constructor. Initializes this to a container that does not contain any object.
+         */
+        Custom() : base_t(Flagger::flagFor((void*)0)) { }
+
+        /**
+         * Removes the contained object, if any.
+         */
+        ~Custom() {
+            if (!this->clear()) {
+                __builtin_trap();
+                // std::cerr << "could not delete data of type " << this->flags() << std::endl;
+            }
+        }
+
+        /**
+         * Returns whether there is any object contained.
+         */
+        inline bool used() const {
+            return this->flags() != Flagger::flagFor((void*)0);
+        }
+
+        /**
+         * Removes the contained object, if any. Returns true if there are no objects contained,
+         * or false on any error (this is highly unexpected).
+         */
+        bool clear() {
+            if (this->used()) {
+                if (_AData_deleter<Flagger, data_t, Ts...>::del(this->flags(), this->get())) {
+                    this->setFlags(Flagger::flagFor((void*)0));
+                    return true;
+                }
+                return false;
+            }
+            return true;
+        }
+
+        template<typename T>
+        using is_supported_by_flagger =
+            typename std::enable_if<Flagger::flagFor((T*)0) != Flagger::flagFor((void*)0)>::type;
+
+        /**
+         * Checks if there is a copiable object of type T in this container. If there is, it copies
+         * that object into the provided address and returns true. Otherwise, it does nothing and
+         * returns false.
+         *
+         * This method normally requires a flag equality between the stored and retrieved types.
+         * However, it also allows retrieving the stored object as the stored type
+         * (usually base type).
+         *
+         * \param T type of object to sought
+         * \param data address at which the object should be retrieved
+         *
+         * \return true if the object was retrieved. false if it was not.
+         */
+        template<
+                typename T,
+                typename=is_supported_by_flagger<T>>
+        bool find(T *data) const {
+            using B = typename Flagger::template store<T>::as_type;
+            if (this->flags() == Flagger::flagFor((T*)0) ||
+                Flagger::canDeleteAs(this->flags(), Flagger::flagFor((T*)0))) {
+                _AData_copier<T>::assign(data, this->get().template get<B>());
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Checks if there is an object of type T in this container. If there is, it moves that
+         * object into the provided address and returns true. Otherwise, it does nothing and returns
+         * false.
+         *
+         * This method normally requires a flag equality between the stored and retrieved types.
+         * However, it also allows retrieving the stored object as the stored type
+         * (usually base type).
+         *
+         * \param T type of object to sought
+         * \param data address at which the object should be retrieved.
+         *
+         * \return true if the object was retrieved. false if it was not.
+         */
+        template<
+                typename T,
+                typename=is_supported_by_flagger<T>>
+        bool remove(T *data) {
+            using B = typename Flagger::template store<T>::as_type;
+            if (this->flags() == Flagger::flagFor((T*)0) ||
+                Flagger::canDeleteAs(this->flags(), Flagger::flagFor((T*)0))) {
+                _AData_mover<T>::assign(data, std::move(this->get().template get<B>()));
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Stores an object into this container by copying. If it was successful, returns true.
+         * Otherwise, (e.g. it could not destroy the already stored object) it returns false. This
+         * latter would be highly unexpected.
+         *
+         * \param T type of object to store
+         * \param data object to store
+         *
+         * \return true if the object was stored. false if it was not.
+         */
+        template<
+                typename T,
+                typename=is_supported_by_flagger<T>,
+                typename=typename std::enable_if<
+                        std::is_copy_constructible<T>::value ||
+                        (std::is_default_constructible<T>::value &&
+                                std::is_copy_assignable<T>::value)>::type>
+        bool set(const T &data) {
+            using B = typename Flagger::template store<T>::as_type;
+
+            // if already contains an object of this type, simply assign
+            if (this->flags() == Flagger::flagFor((T*)0) && std::is_same<T, B>::value) {
+                _AData_copier<B>::assign(&this->get().template get<B>(), data);
+                return true;
+            } else if (this->used()) {
+                // destroy previous object
+                if (!this->clear()) {
+                    return false;
+                }
+            }
+            this->get().template emplace<B>(data);
+            this->setFlags(Flagger::flagFor((T *)0));
+            return true;
+        }
+
+        /**
+         * Moves an object into this container. If it was successful, returns true. Otherwise,
+         * (e.g. it could not destroy the already stored object) it returns false. This latter
+         * would be highly unexpected.
+         *
+         * \param T type of object to store
+         * \param data object to store
+         *
+         * \return true if the object was stored. false if it was not.
+         */
+        template<
+                typename T,
+                typename=is_supported_by_flagger<T>>
+        bool set(T &&data) {
+            using B = typename Flagger::template store<T>::as_type;
+
+            // if already contains an object of this type, simply assign
+            if (this->flags() == Flagger::flagFor((T*)0) && std::is_same<T, B>::value) {
+                _AData_mover<B>::assign(&this->get().template get<B>(), std::forward<T&&>(data));
+                return true;
+            } else if (this->used()) {
+                // destroy previous object
+                if (!this->clear()) {
+                    return false;
+                }
+            }
+            this->get().template emplace<B>(std::forward<T&&>(data));
+            this->setFlags(Flagger::flagFor((T *)0));
+            return true;
+        }
+    };
+
+    /**
+     * Basic AData using the default type flagger and requested flag type.
+     *
+     * \param Flag desired flag type to use. Must be an unsigned and std::integral type.
+     */
+    template<typename Flag>
+    using Strict = Custom<flagger<Flag>>;
+
+    /**
+     * Basic AData using the default type flagger and uint32_t flag.
+     */
+    using Basic = Strict<uint32_t>;
+
+    /**
+     * AData using the relaxed type flagger for max size and requested flag type.
+     *
+     * \param Flag desired flag type to use. Must be an unsigned and std::integral type.
+     */
+    template<typename Flag, size_t MaxSize = 1024, size_t Align = 4>
+    using Relaxed = Custom<relaxed_flagger<Flag, MaxSize, Align>>;
+
+    /**
+     * Basic AData using the relaxed type flagger and uint32_t flag.
+     */
+    using RelaxedBasic = Relaxed<uint32_t>;
+};
+
+}  // namespace android
+
+#endif  // STAGEFRIGHT_FOUNDATION_A_DATA_H_
+
diff --git a/include/media/stagefright/foundation/TypeTraits.h b/include/media/stagefright/foundation/TypeTraits.h
index 2eaec35..1250e9b 100644
--- a/include/media/stagefright/foundation/TypeTraits.h
+++ b/include/media/stagefright/foundation/TypeTraits.h
@@ -85,6 +85,139 @@
             typename underlying_integral_type<T, signed>::type>::value> {
 };
 
+/**
+ * Type support relationship query template.
+ *
+ * If T occurs as one of the types in Us with the same const-volatile qualifications, provides the
+ * member constant |value| equal to true. Otherwise value is false.
+ */
+template<typename T, typename ...Us>
+struct is_one_of;
+
+/// \if 0
+/**
+ * Template specialization when first type matches the searched type.
+ */
+template<typename T, typename ...Us>
+struct is_one_of<T, T, Us...> : std::true_type {};
+
+/**
+ * Template specialization when first type does not match the searched type.
+ */
+template<typename T, typename U, typename ...Us>
+struct is_one_of<T, U, Us...> : is_one_of<T, Us...> {};
+
+/**
+ * Template specialization when there are no types to search.
+ */
+template<typename T>
+struct is_one_of<T> : std::false_type {};
+/// \endif
+
+/**
+ * Type support relationship query template.
+ *
+ * If all types in Us are unique, provides the member constant |value| equal to true.
+ * Otherwise value is false.
+ */
+template<typename ...Us>
+struct are_unique;
+
+/// \if 0
+/**
+ * Template specialization when there are no types.
+ */
+template<>
+struct are_unique<> : std::true_type {};
+
+/**
+ * Template specialization when there is at least one type to check.
+ */
+template<typename T, typename ...Us>
+struct are_unique<T, Us...>
+    : std::integral_constant<bool, are_unique<Us...>::value && !is_one_of<T, Us...>::value> {};
+/// \endif
+
+/// \if 0
+template<size_t Base, typename T, typename ...Us>
+struct _find_first_impl;
+
+/**
+ * Template specialization when there are no types to search.
+ */
+template<size_t Base, typename T>
+struct _find_first_impl<Base, T> : std::integral_constant<size_t, 0> {};
+
+/**
+ * Template specialization when T is the first type in Us.
+ */
+template<size_t Base, typename T, typename ...Us>
+struct _find_first_impl<Base, T, T, Us...> : std::integral_constant<size_t, Base> {};
+
+/**
+ * Template specialization when T is not the first type in Us.
+ */
+template<size_t Base, typename T, typename U, typename ...Us>
+struct _find_first_impl<Base, T, U, Us...>
+    : std::integral_constant<size_t, _find_first_impl<Base + 1, T, Us...>::value> {};
+
+/// \endif
+
+/**
+ * Type support relationship query template.
+ *
+ * If T occurs in Us, index is the 1-based left-most index of T in Us. Otherwise, index is 0.
+ */
+template<typename T, typename ...Us>
+struct find_first {
+    static constexpr size_t index = _find_first_impl<1, T, Us...>::value;
+};
+
+/// \if 0
+/**
+ * Helper class for find_first_convertible_to template.
+ *
+ * Adds a base index.
+ */
+template<size_t Base, typename T, typename ...Us>
+struct _find_first_convertible_to_helper;
+
+/**
+ * Template specialization for when there are more types to consider
+ */
+template<size_t Base, typename T, typename U, typename ...Us>
+struct _find_first_convertible_to_helper<Base, T, U, Us...> {
+    static constexpr size_t index =
+        std::is_convertible<T, U>::value ? Base :
+                _find_first_convertible_to_helper<Base + 1, T, Us...>::index;
+    typedef typename std::conditional<
+        std::is_convertible<T, U>::value, U,
+        typename _find_first_convertible_to_helper<Base + 1, T, Us...>::type>::type type;
+};
+
+/**
+ * Template specialization for when there are no more types to consider
+ */
+template<size_t Base, typename T>
+struct _find_first_convertible_to_helper<Base, T> {
+    static constexpr size_t index = 0;
+    typedef void type;
+};
+
+/// \endif
+
+/**
+ * Type support template that returns the type that T can be implicitly converted into, and its
+ * index, from a list of other types (Us).
+ *
+ * Returns index of 0 and type of void if there are no convertible types.
+ *
+ * \param T type that is converted
+ * \param Us types into which the conversion is considered
+ */
+template<typename T, typename ...Us>
+struct find_first_convertible_to : public _find_first_convertible_to_helper<1, T, Us...> { };
+
 }  // namespace android
 
 #endif  // STAGEFRIGHT_FOUNDATION_TYPE_TRAITS_H_
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 3c7e8b7..aef7dfb 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -671,6 +671,8 @@
         mState = STATE_STOPPING;
     } else {
         mState = STATE_STOPPED;
+        ALOGD_IF(mSharedBuffer == nullptr,
+                "stop() called with %u frames delivered", mReleased.value());
         mReleased = 0;
     }
 
diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp
deleted file mode 100644
index e943eed..0000000
--- a/media/libaudiohal/Android.bp
+++ /dev/null
@@ -1,24 +0,0 @@
-cc_library_shared {
-    name: "libaudiohal",
-
-    srcs: [
-        "DeviceHalLocal.cpp",
-        "DevicesFactoryHalLocal.cpp",
-        "EffectHalLocal.cpp",
-        "EffectsFactoryHalLocal.cpp",
-        "StreamHalLocal.cpp",
-    ],
-
-    shared_libs: [
-        "libcutils",
-        "libhardware",
-        "liblog",
-        "libeffects",
-        "libutils",
-    ],
-
-    cflags: [
-        "-Werror",
-        "-Wall",
-    ],
-}
diff --git a/media/libaudiohal/Android.mk b/media/libaudiohal/Android.mk
new file mode 100644
index 0000000..63aa9c6
--- /dev/null
+++ b/media/libaudiohal/Android.mk
@@ -0,0 +1,50 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libhardware \
+    liblog \
+    libutils \
+    libeffects
+
+ifeq ($(ENABLE_TREBLE), true)
+
+LOCAL_CFLAGS += -DENABLE_TREBLE
+
+LOCAL_SRC_FILES := \
+    ConversionHelperHidl.cpp   \
+    DeviceHalHidl.cpp          \
+    DevicesFactoryHalHidl.cpp  \
+    EffectHalHidl.cpp          \
+    EffectsFactoryHalHidl.cpp  \
+    StreamHalHidl.cpp
+
+LOCAL_SHARED_LIBRARIES += \
+    libhwbinder      \
+    libhidlbase      \
+    libhidltransport \
+    libbase          \
+    android.hardware.audio@2.0             \
+    android.hardware.audio.common@2.0      \
+    android.hardware.audio.common@2.0-util \
+    android.hardware.audio.effect@2.0
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
+
+else  # if !ENABLE_TREBLE
+
+LOCAL_SRC_FILES := \
+    DeviceHalLocal.cpp          \
+    DevicesFactoryHalLocal.cpp  \
+    EffectHalLocal.cpp          \
+    EffectsFactoryHalLocal.cpp  \
+    StreamHalLocal.cpp
+endif  # ENABLE_TREBLE
+
+LOCAL_MODULE := libaudiohal
+
+LOCAL_CFLAGS := -Wall -Werror
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libaudiohal/ConversionHelperHidl.cpp b/media/libaudiohal/ConversionHelperHidl.cpp
new file mode 100644
index 0000000..ebbc02c
--- /dev/null
+++ b/media/libaudiohal/ConversionHelperHidl.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 <string.h>
+
+#define LOG_TAG "HalHidl"
+#include <media/AudioParameter.h>
+#include <utils/Log.h>
+
+#include "ConversionHelperHidl.h"
+
+using ::android::hardware::audio::V2_0::Result;
+
+namespace android {
+
+// static
+status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec<hidl_string> *hidlKeys) {
+    AudioParameter halKeys(keys);
+    if (halKeys.size() == 0) return BAD_VALUE;
+    hidlKeys->resize(halKeys.size());
+    for (size_t i = 0; i < halKeys.size(); ++i) {
+        String8 key;
+        status_t status = halKeys.getAt(i, key);
+        if (status != OK) return status;
+        (*hidlKeys)[i] = key.string();
+    }
+    return OK;
+}
+
+// static
+status_t ConversionHelperHidl::parametersFromHal(
+        const String8& kvPairs, hidl_vec<ParameterValue> *hidlParams) {
+    AudioParameter params(kvPairs);
+    if (params.size() == 0) return BAD_VALUE;
+    hidlParams->resize(params.size());
+    for (size_t i = 0; i < params.size(); ++i) {
+        String8 key, value;
+        status_t status = params.getAt(i, key, value);
+        if (status != OK) return status;
+        (*hidlParams)[i].key = key.string();
+        (*hidlParams)[i].value = value.string();
+    }
+    return OK;
+}
+
+// static
+void ConversionHelperHidl::parametersToHal(
+        const hidl_vec<ParameterValue>& parameters, String8 *values) {
+    AudioParameter params;
+    for (size_t i = 0; i < parameters.size(); ++i) {
+        params.add(String8(parameters[i].key.c_str()), String8(parameters[i].value.c_str()));
+    }
+    values->setTo(params.toString());
+}
+
+ConversionHelperHidl::ConversionHelperHidl(const char* className)
+        : mClassName(className) {
+}
+
+// static
+status_t ConversionHelperHidl::analyzeResult(const Result& result) {
+    switch (result) {
+        case Result::OK: return OK;
+        case Result::INVALID_ARGUMENTS: return BAD_VALUE;
+        case Result::INVALID_STATE: return NOT_ENOUGH_DATA;
+        case Result::NOT_INITIALIZED: return NO_INIT;
+        case Result::NOT_SUPPORTED: return INVALID_OPERATION;
+        default: return NO_INIT;
+    }
+}
+
+status_t ConversionHelperHidl::processReturn(const char* funcName, const Status& status) {
+    const status_t st = status.transactionError();
+    ALOGE_IF(st, "%s %p %s: %s (from rpc)", mClassName, this, funcName, strerror(-st));
+    return st;
+}
+
+status_t ConversionHelperHidl::processReturn(
+        const char* funcName, const Status& status, hardware::audio::V2_0::Result retval) {
+    const status_t st = status.isOk() ? analyzeResult(retval) : status.transactionError();
+    ALOGE_IF(st, "%s %p %s: %s (from %s)",
+            mClassName, this, funcName, strerror(-st), status.isOk() ? "hal" : "rpc");
+    return st;
+}
+
+}  // namespace android
diff --git a/media/libaudiohal/ConversionHelperHidl.h b/media/libaudiohal/ConversionHelperHidl.h
new file mode 100644
index 0000000..628913a
--- /dev/null
+++ b/media/libaudiohal/ConversionHelperHidl.h
@@ -0,0 +1,75 @@
+/*
+ * 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_CONVERSION_HELPER_HIDL_H
+#define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H
+
+#include <android/hardware/audio/2.0/types.h>
+#include <hidl/HidlSupport.h>
+#include <utils/String8.h>
+
+using ::android::hardware::audio::V2_0::ParameterValue;
+using ::android::hardware::Return;
+using ::android::hardware::Status;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+namespace android {
+
+class ConversionHelperHidl {
+  protected:
+    static status_t keysFromHal(const String8& keys, hidl_vec<hidl_string> *hidlKeys);
+    static status_t parametersFromHal(const String8& kvPairs, hidl_vec<ParameterValue> *hidlParams);
+    static void parametersToHal(const hidl_vec<ParameterValue>& parameters, String8 *values);
+
+    ConversionHelperHidl(const char* className);
+
+    status_t processReturn(const char* funcName, const Return<void>& ret) {
+        return processReturn(funcName, ret.getStatus());
+    }
+
+    template<typename R, typename T>
+    status_t processReturn(const char* funcName, const Return<R>& ret, T *retval) {
+        if (ret.getStatus().isOk()) {
+            // This way it also works for enum class to unscoped enum conversion.
+            *retval = static_cast<T>(static_cast<R>(ret));
+            return OK;
+        }
+        return processReturn(funcName, ret.getStatus());
+    }
+
+    status_t processReturn(const char* funcName, const Return<hardware::audio::V2_0::Result>& ret) {
+        return processReturn(funcName, ret, ret);
+    }
+
+    template<typename T>
+    status_t processReturn(
+            const char* funcName, const Return<T>& ret, hardware::audio::V2_0::Result retval) {
+        return processReturn(funcName, ret.getStatus(), retval);
+    }
+
+  private:
+    const char* mClassName;
+
+    static status_t analyzeResult(const hardware::audio::V2_0::Result& result);
+    status_t processReturn(const char* funcName, const Status& status);
+    status_t processReturn(
+            const char* funcName, const Status& status, hardware::audio::V2_0::Result retval);
+};
+
+}  // namespace android
+
+#endif // ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H
diff --git a/media/libaudiohal/DeviceHalHidl.cpp b/media/libaudiohal/DeviceHalHidl.cpp
new file mode 100644
index 0000000..a6ced12
--- /dev/null
+++ b/media/libaudiohal/DeviceHalHidl.cpp
@@ -0,0 +1,351 @@
+/*
+ * 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 <stdio.h>
+
+#define LOG_TAG "DeviceHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware/audio/2.0/IPrimaryDevice.h>
+#include <cutils/native_handle.h>
+#include <utils/Log.h>
+
+#include "DeviceHalHidl.h"
+#include "HidlUtils.h"
+#include "StreamHalHidl.h"
+
+using ::android::hardware::audio::common::V2_0::AudioConfig;
+using ::android::hardware::audio::common::V2_0::AudioDevice;
+using ::android::hardware::audio::common::V2_0::AudioInputFlag;
+using ::android::hardware::audio::common::V2_0::AudioOutputFlag;
+using ::android::hardware::audio::common::V2_0::AudioPatchHandle;
+using ::android::hardware::audio::common::V2_0::AudioPort;
+using ::android::hardware::audio::common::V2_0::AudioPortConfig;
+using ::android::hardware::audio::common::V2_0::AudioMode;
+using ::android::hardware::audio::common::V2_0::AudioSource;
+using ::android::hardware::audio::V2_0::DeviceAddress;
+using ::android::hardware::audio::V2_0::IPrimaryDevice;
+using ::android::hardware::audio::V2_0::ParameterValue;
+using ::android::hardware::audio::V2_0::Result;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+namespace android {
+
+namespace {
+
+status_t deviceAddressFromHal(
+        audio_devices_t device, const char* halAddress, DeviceAddress* address) {
+    address->device = AudioDevice(device);
+    const bool isInput = (device & AUDIO_DEVICE_BIT_IN) != 0;
+    if (isInput) device &= ~AUDIO_DEVICE_BIT_IN;
+    if ((!isInput && (device & AUDIO_DEVICE_OUT_ALL_A2DP) != 0)
+            || (isInput && (device & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) != 0)) {
+        int status = sscanf(halAddress,
+                "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX",
+                &address->address.mac[0], &address->address.mac[1], &address->address.mac[2],
+                &address->address.mac[3], &address->address.mac[4], &address->address.mac[5]);
+        return status == 6 ? OK : BAD_VALUE;
+    } else if ((!isInput && (device & AUDIO_DEVICE_OUT_IP) != 0)
+            || (isInput && (device & AUDIO_DEVICE_IN_IP) != 0)) {
+        int status = sscanf(halAddress,
+                "%hhu.%hhu.%hhu.%hhu",
+                &address->address.ipv4[0], &address->address.ipv4[1],
+                &address->address.ipv4[2], &address->address.ipv4[3]);
+        return status == 4 ? OK : BAD_VALUE;
+    } else if ((!isInput && (device & AUDIO_DEVICE_OUT_ALL_USB)) != 0
+            || (isInput && (device & AUDIO_DEVICE_IN_ALL_USB)) != 0) {
+        int status = sscanf(halAddress,
+                "card=%d;device=%d",
+                &address->address.alsa.card, &address->address.alsa.device);
+        return status == 2 ? OK : BAD_VALUE;
+    } else if ((!isInput && (device & AUDIO_DEVICE_OUT_BUS) != 0)
+            || (isInput && (device & AUDIO_DEVICE_IN_BUS) != 0)) {
+        if (halAddress != NULL) {
+            address->busAddress = halAddress;
+            return OK;
+        }
+        return BAD_VALUE;
+    } else if ((!isInput && (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) != 0
+            || (isInput && (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) != 0)) {
+        if (halAddress != NULL) {
+            address->rSubmixAddress = halAddress;
+            return OK;
+        }
+        return BAD_VALUE;
+    }
+    return OK;
+}
+
+}  // namespace
+
+DeviceHalHidl::DeviceHalHidl(const sp<IDevice>& device)
+        : ConversionHelperHidl("Device"), mDevice(device) {
+}
+
+DeviceHalHidl::~DeviceHalHidl() {
+}
+
+status_t DeviceHalHidl::getSupportedDevices(uint32_t*) {
+    // Obsolete.
+    return INVALID_OPERATION;
+}
+
+status_t DeviceHalHidl::initCheck() {
+    if (mDevice == 0) return NO_INIT;
+    return processReturn("initCheck", mDevice->initCheck());
+}
+
+status_t DeviceHalHidl::setVoiceVolume(float volume) {
+    if (mDevice == 0) return NO_INIT;
+    sp<IPrimaryDevice> primaryDev = IPrimaryDevice::castFrom(mDevice);
+    if (primaryDev == 0) return INVALID_OPERATION;
+    return processReturn("setVoiceVolume", primaryDev->setVoiceVolume(volume));
+}
+
+status_t DeviceHalHidl::setMasterVolume(float volume) {
+    if (mDevice == 0) return NO_INIT;
+    sp<IPrimaryDevice> primaryDev = IPrimaryDevice::castFrom(mDevice);
+    if (primaryDev == 0) return INVALID_OPERATION;
+    return processReturn("setMasterVolume", primaryDev->setMasterVolume(volume));
+}
+
+status_t DeviceHalHidl::getMasterVolume(float *volume) {
+    if (mDevice == 0) return NO_INIT;
+    sp<IPrimaryDevice> primaryDev = IPrimaryDevice::castFrom(mDevice);
+    if (primaryDev == 0) return INVALID_OPERATION;
+    Result retval;
+    Return<void> ret = primaryDev->getMasterVolume(
+            [&](Result r, float v) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *volume = v;
+                }
+            });
+    return processReturn("getMasterVolume", ret, retval);
+}
+
+status_t DeviceHalHidl::setMode(audio_mode_t mode) {
+    if (mDevice == 0) return NO_INIT;
+    sp<IPrimaryDevice> primaryDev = IPrimaryDevice::castFrom(mDevice);
+    if (primaryDev == 0) return INVALID_OPERATION;
+    return processReturn("setMode", primaryDev->setMode(AudioMode(mode)));
+}
+
+status_t DeviceHalHidl::setMicMute(bool state) {
+    if (mDevice == 0) return NO_INIT;
+    return processReturn("setMicMute", mDevice->setMicMute(state));
+}
+
+status_t DeviceHalHidl::getMicMute(bool *state) {
+    if (mDevice == 0) return NO_INIT;
+    Result retval;
+    Return<void> ret = mDevice->getMicMute(
+            [&](Result r, bool mute) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *state = mute;
+                }
+            });
+    return processReturn("getMicMute", ret, retval);
+}
+
+status_t DeviceHalHidl::setMasterMute(bool state) {
+    if (mDevice == 0) return NO_INIT;
+    return processReturn("setMasterMute", mDevice->setMasterMute(state));
+}
+
+status_t DeviceHalHidl::getMasterMute(bool *state) {
+    if (mDevice == 0) return NO_INIT;
+    Result retval;
+    Return<void> ret = mDevice->getMasterMute(
+            [&](Result r, bool mute) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *state = mute;
+                }
+            });
+    return processReturn("getMasterMute", ret, retval);
+}
+
+status_t DeviceHalHidl::setParameters(const String8& kvPairs) {
+    if (mDevice == 0) return NO_INIT;
+    hidl_vec<ParameterValue> hidlParams;
+    status_t status = parametersFromHal(kvPairs, &hidlParams);
+    if (status != OK) return status;
+    return processReturn("setParameters", mDevice->setParameters(hidlParams));
+}
+
+status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) {
+    values->clear();
+    if (mDevice == 0) return NO_INIT;
+    hidl_vec<hidl_string> hidlKeys;
+    status_t status = keysFromHal(keys, &hidlKeys);
+    if (status != OK) return status;
+    Result retval;
+    Return<void> ret = mDevice->getParameters(
+            hidlKeys,
+            [&](Result r, const hidl_vec<ParameterValue>& parameters) {
+                retval = r;
+                if (retval == Result::OK) {
+                    parametersToHal(parameters, values);
+                }
+            });
+    return processReturn("getParameters", ret, retval);
+}
+
+status_t DeviceHalHidl::getInputBufferSize(
+        const struct audio_config *config, size_t *size) {
+    if (mDevice == 0) return NO_INIT;
+    AudioConfig hidlConfig;
+    HidlUtils::audioConfigFromHal(*config, &hidlConfig);
+    Result retval;
+    Return<void> ret = mDevice->getInputBufferSize(
+            hidlConfig,
+            [&](Result r, uint64_t bufferSize) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *size = static_cast<size_t>(bufferSize);
+                }
+            });
+    return processReturn("getInputBufferSize", ret, retval);
+}
+
+status_t DeviceHalHidl::openOutputStream(
+        audio_io_handle_t handle,
+        audio_devices_t devices,
+        audio_output_flags_t flags,
+        struct audio_config *config,
+        const char *address,
+        sp<StreamOutHalInterface> *outStream) {
+    if (mDevice == 0) return NO_INIT;
+    DeviceAddress hidlDevice;
+    status_t status = deviceAddressFromHal(devices, address, &hidlDevice);
+    if (status != OK) return status;
+    AudioConfig hidlConfig;
+    HidlUtils::audioConfigFromHal(*config, &hidlConfig);
+    Result retval = Result::NOT_INITIALIZED;
+    Return<void> ret = mDevice->openOutputStream(
+            handle,
+            hidlDevice,
+            hidlConfig,
+            AudioOutputFlag(flags),
+            [&](Result r, const sp<IStreamOut>& result, const AudioConfig& suggestedConfig) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *outStream = new StreamOutHalHidl(result);
+                }
+                HidlUtils::audioConfigToHal(suggestedConfig, config);
+            });
+    return processReturn("openOutputStream", ret, retval);
+}
+
+status_t DeviceHalHidl::openInputStream(
+        audio_io_handle_t handle,
+        audio_devices_t devices,
+        struct audio_config *config,
+        audio_input_flags_t flags,
+        const char *address,
+        audio_source_t source,
+        sp<StreamInHalInterface> *inStream) {
+    if (mDevice == 0) return NO_INIT;
+    DeviceAddress hidlDevice;
+    status_t status = deviceAddressFromHal(devices, address, &hidlDevice);
+    if (status != OK) return status;
+    AudioConfig hidlConfig;
+    HidlUtils::audioConfigFromHal(*config, &hidlConfig);
+    Result retval = Result::NOT_INITIALIZED;
+    Return<void> ret = mDevice->openInputStream(
+            handle,
+            hidlDevice,
+            hidlConfig,
+            AudioInputFlag(flags),
+            AudioSource(source),
+            [&](Result r, const sp<IStreamIn>& result, const AudioConfig& suggestedConfig) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *inStream = new StreamInHalHidl(result);
+                }
+                HidlUtils::audioConfigToHal(suggestedConfig, config);
+            });
+    return processReturn("openInputStream", ret, retval);
+}
+
+status_t DeviceHalHidl::supportsAudioPatches(bool *supportsPatches) {
+    if (mDevice == 0) return NO_INIT;
+    return processReturn("supportsAudioPatches", mDevice->supportsAudioPatches(), supportsPatches);
+}
+
+status_t DeviceHalHidl::createAudioPatch(
+        unsigned int num_sources,
+        const struct audio_port_config *sources,
+        unsigned int num_sinks,
+        const struct audio_port_config *sinks,
+        audio_patch_handle_t *patch) {
+    if (mDevice == 0) return NO_INIT;
+    hidl_vec<AudioPortConfig> hidlSources, hidlSinks;
+    HidlUtils::audioPortConfigsFromHal(num_sources, sources, &hidlSources);
+    HidlUtils::audioPortConfigsFromHal(num_sinks, sinks, &hidlSinks);
+    Result retval;
+    Return<void> ret = mDevice->createAudioPatch(
+            hidlSources, hidlSinks,
+            [&](Result r, AudioPatchHandle hidlPatch) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *patch = static_cast<audio_patch_handle_t>(hidlPatch);
+                }
+            });
+    return processReturn("createAudioPatch", ret, retval);
+}
+
+status_t DeviceHalHidl::releaseAudioPatch(audio_patch_handle_t patch) {
+    if (mDevice == 0) return NO_INIT;
+    return processReturn("releaseAudioPatch", mDevice->releaseAudioPatch(patch));
+}
+
+status_t DeviceHalHidl::getAudioPort(struct audio_port *port) {
+    if (mDevice == 0) return NO_INIT;
+    AudioPort hidlPort;
+    HidlUtils::audioPortFromHal(*port, &hidlPort);
+    Result retval;
+    Return<void> ret = mDevice->getAudioPort(
+            hidlPort,
+            [&](Result r, const AudioPort& p) {
+                retval = r;
+                if (retval == Result::OK) {
+                    HidlUtils::audioPortToHal(p, port);
+                }
+            });
+    return processReturn("getAudioPort", ret, retval);
+}
+
+status_t DeviceHalHidl::setAudioPortConfig(const struct audio_port_config *config) {
+    if (mDevice == 0) return NO_INIT;
+    AudioPortConfig hidlConfig;
+    HidlUtils::audioPortConfigFromHal(*config, &hidlConfig);
+    return processReturn("setAudioPortConfig", mDevice->setAudioPortConfig(hidlConfig));
+}
+
+status_t DeviceHalHidl::dump(int fd) {
+    if (mDevice == 0) return NO_INIT;
+    native_handle_t* hidlHandle = native_handle_create(1, 0);
+    hidlHandle->data[0] = fd;
+    Return<void> ret = mDevice->debugDump(hidlHandle);
+    native_handle_delete(hidlHandle);
+    return processReturn("dump", ret);
+}
+
+} // namespace android
diff --git a/media/libaudiohal/DeviceHalHidl.h b/media/libaudiohal/DeviceHalHidl.h
new file mode 100644
index 0000000..9da02a4
--- /dev/null
+++ b/media/libaudiohal/DeviceHalHidl.h
@@ -0,0 +1,123 @@
+/*
+ * 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_DEVICE_HAL_HIDL_H
+#define ANDROID_HARDWARE_DEVICE_HAL_HIDL_H
+
+#include <android/hardware/audio/2.0/IDevice.h>
+#include <media/audiohal/DeviceHalInterface.h>
+
+#include "ConversionHelperHidl.h"
+
+using ::android::hardware::audio::V2_0::IDevice;
+using ::android::hardware::Return;
+
+namespace android {
+
+class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl
+{
+  public:
+    // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t.
+    virtual status_t getSupportedDevices(uint32_t *devices);
+
+    // Check to see if the audio hardware interface has been initialized.
+    virtual status_t initCheck();
+
+    // Set the audio volume of a voice call. Range is between 0.0 and 1.0.
+    virtual status_t setVoiceVolume(float volume);
+
+    // Set the audio volume for all audio activities other than voice call.
+    virtual status_t setMasterVolume(float volume);
+
+    // Get the current master volume value for the HAL.
+    virtual status_t getMasterVolume(float *volume);
+
+    // Called when the audio mode changes.
+    virtual status_t setMode(audio_mode_t mode);
+
+    // Muting control.
+    virtual status_t setMicMute(bool state);
+    virtual status_t getMicMute(bool *state);
+    virtual status_t setMasterMute(bool state);
+    virtual status_t getMasterMute(bool *state);
+
+    // Set global audio parameters.
+    virtual status_t setParameters(const String8& kvPairs);
+
+    // Get global audio parameters.
+    virtual status_t getParameters(const String8& keys, String8 *values);
+
+    // Returns audio input buffer size according to parameters passed.
+    virtual status_t getInputBufferSize(const struct audio_config *config,
+            size_t *size);
+
+    // Creates and opens the audio hardware output stream. The stream is closed
+    // by releasing all references to the returned object.
+    virtual status_t openOutputStream(
+            audio_io_handle_t handle,
+            audio_devices_t devices,
+            audio_output_flags_t flags,
+            struct audio_config *config,
+            const char *address,
+            sp<StreamOutHalInterface> *outStream);
+
+    // Creates and opens the audio hardware input stream. The stream is closed
+    // by releasing all references to the returned object.
+    virtual status_t openInputStream(
+            audio_io_handle_t handle,
+            audio_devices_t devices,
+            struct audio_config *config,
+            audio_input_flags_t flags,
+            const char *address,
+            audio_source_t source,
+            sp<StreamInHalInterface> *inStream);
+
+    // Returns whether createAudioPatch and releaseAudioPatch operations are supported.
+    virtual status_t supportsAudioPatches(bool *supportsPatches);
+
+    // Creates an audio patch between several source and sink ports.
+    virtual status_t createAudioPatch(
+            unsigned int num_sources,
+            const struct audio_port_config *sources,
+            unsigned int num_sinks,
+            const struct audio_port_config *sinks,
+            audio_patch_handle_t *patch);
+
+    // Releases an audio patch.
+    virtual status_t releaseAudioPatch(audio_patch_handle_t patch);
+
+    // Fills the list of supported attributes for a given audio port.
+    virtual status_t getAudioPort(struct audio_port *port);
+
+    // Set audio port configuration.
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+
+    virtual status_t dump(int fd);
+
+  private:
+    friend class DevicesFactoryHalHidl;
+    sp<IDevice> mDevice;
+
+    // Can not be constructed directly by clients.
+    explicit DeviceHalHidl(const sp<IDevice>& device);
+
+    // The destructor automatically closes the device.
+    virtual ~DeviceHalHidl();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICE_HAL_HIDL_H
diff --git a/media/libaudiohal/DeviceHalLocal.cpp b/media/libaudiohal/DeviceHalLocal.cpp
index 78adfef..fc098f5 100644
--- a/media/libaudiohal/DeviceHalLocal.cpp
+++ b/media/libaudiohal/DeviceHalLocal.cpp
@@ -109,11 +109,17 @@
         const char *address,
         sp<StreamOutHalInterface> *outStream) {
     audio_stream_out_t *halStream;
+    ALOGV("open_output_stream handle: %d devices: %x flags: %#x"
+            "srate: %d format %#x channels %x address %s",
+            handle, devices, flags,
+            config->sample_rate, config->format, config->channel_mask,
+            address);
     int openResut = mDev->open_output_stream(
             mDev, handle, devices, flags, config, &halStream, address);
     if (openResut == OK) {
         *outStream = new StreamOutHalLocal(halStream, this);
     }
+    ALOGV("open_output_stream status %d stream %p", openResut, halStream);
     return openResut;
 }
 
@@ -126,11 +132,17 @@
         audio_source_t source,
         sp<StreamInHalInterface> *inStream) {
     audio_stream_in_t *halStream;
+    ALOGV("open_input_stream handle: %d devices: %x flags: %#x "
+            "srate: %d format %#x channels %x address %s source %d",
+            handle, devices, flags,
+            config->sample_rate, config->format, config->channel_mask,
+            address, source);
     int openResult = mDev->open_input_stream(
             mDev, handle, devices, config, &halStream, flags, address, source);
     if (openResult == OK) {
         *inStream = new StreamInHalLocal(halStream, this);
     }
+    ALOGV("open_input_stream status %d stream %p", openResult, inStream);
     return openResult;
 }
 
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.cpp b/media/libaudiohal/DevicesFactoryHalHidl.cpp
new file mode 100644
index 0000000..155b1a8
--- /dev/null
+++ b/media/libaudiohal/DevicesFactoryHalHidl.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 <string.h>
+
+#define LOG_TAG "DevicesFactoryHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware/audio/2.0/IDevice.h>
+#include <utils/Log.h>
+
+#include "DeviceHalHidl.h"
+#include "DevicesFactoryHalHidl.h"
+
+using ::android::hardware::audio::V2_0::IDevice;
+using ::android::hardware::audio::V2_0::Result;
+using ::android::hardware::Return;
+using ::android::hardware::Status;
+
+namespace android {
+
+// static
+sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
+    return new DevicesFactoryHalHidl();
+}
+
+DevicesFactoryHalHidl::DevicesFactoryHalHidl() {
+    mDevicesFactory = IDevicesFactory::getService("audio_devices_factory");
+}
+
+DevicesFactoryHalHidl::~DevicesFactoryHalHidl() {
+}
+
+// static
+status_t DevicesFactoryHalHidl::nameFromHal(const char *name, IDevicesFactory::Device *device) {
+    if (strcmp(name, AUDIO_HARDWARE_MODULE_ID_PRIMARY) == 0) {
+        *device = IDevicesFactory::Device::PRIMARY;
+        return OK;
+    } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_A2DP) == 0) {
+        *device = IDevicesFactory::Device::A2DP;
+        return OK;
+    } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_USB) == 0) {
+        *device = IDevicesFactory::Device::USB;
+        return OK;
+    } else if(strcmp(name, AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX) == 0) {
+        *device = IDevicesFactory::Device::R_SUBMIX;
+        return OK;
+    }
+    ALOGE("Invalid device name %s", name);
+    return BAD_VALUE;
+}
+
+status_t DevicesFactoryHalHidl::openDevice(const char *name, sp<DeviceHalInterface> *device) {
+    if (mDevicesFactory == 0) return NO_INIT;
+    IDevicesFactory::Device hidlDevice;
+    status_t status = nameFromHal(name, &hidlDevice);
+    if (status != OK) return status;
+    Result retval = Result::NOT_INITIALIZED;
+    Return<void> ret = mDevicesFactory->openDevice(
+            hidlDevice,
+            [&](Result r, const sp<IDevice>& result) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *device = new DeviceHalHidl(result);
+                }
+            });
+    if (ret.getStatus().isOk()) {
+        if (retval == Result::OK) return OK;
+        else if (retval == Result::INVALID_ARGUMENTS) return BAD_VALUE;
+        else return NO_INIT;
+    }
+    return ret.getStatus().transactionError();
+}
+
+} // namespace android
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.h b/media/libaudiohal/DevicesFactoryHalHidl.h
new file mode 100644
index 0000000..a26dec1
--- /dev/null
+++ b/media/libaudiohal/DevicesFactoryHalHidl.h
@@ -0,0 +1,53 @@
+/*
+ * 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_DEVICES_FACTORY_HAL_HIDL_H
+#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H
+
+#include <android/hardware/audio/2.0/IDevicesFactory.h>
+#include <media/audiohal/DevicesFactoryHalInterface.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include "DeviceHalHidl.h"
+
+using ::android::hardware::audio::V2_0::IDevicesFactory;
+
+namespace android {
+
+class DevicesFactoryHalHidl : public DevicesFactoryHalInterface
+{
+  public:
+    // Opens a device with the specified name. To close the device, it is
+    // necessary to release references to the returned object.
+    virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
+
+  private:
+    friend class DevicesFactoryHalInterface;
+
+    sp<IDevicesFactory> mDevicesFactory;
+
+    static status_t nameFromHal(const char *name, IDevicesFactory::Device *device);
+
+    // Can not be constructed directly by clients.
+    DevicesFactoryHalHidl();
+
+    virtual ~DevicesFactoryHalHidl();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HIDL_H
diff --git a/media/libaudiohal/DevicesFactoryHalLocal.h b/media/libaudiohal/DevicesFactoryHalLocal.h
index 690cd34..58ce4ff 100644
--- a/media/libaudiohal/DevicesFactoryHalLocal.h
+++ b/media/libaudiohal/DevicesFactoryHalLocal.h
@@ -28,8 +28,6 @@
 class DevicesFactoryHalLocal : public DevicesFactoryHalInterface
 {
   public:
-    virtual ~DevicesFactoryHalLocal() {}
-
     // Opens a device with the specified name. To close the device, it is
     // necessary to release references to the returned object.
     virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
@@ -39,6 +37,8 @@
 
     // Can not be constructed directly by clients.
     DevicesFactoryHalLocal() {}
+
+    virtual ~DevicesFactoryHalLocal() {}
 };
 
 } // namespace android
diff --git a/media/libaudiohal/EffectHalHidl.cpp b/media/libaudiohal/EffectHalHidl.cpp
new file mode 100644
index 0000000..b508cb5
--- /dev/null
+++ b/media/libaudiohal/EffectHalHidl.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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 "EffectHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <media/EffectsFactoryApi.h>
+#include <utils/Log.h>
+
+#include "EffectHalHidl.h"
+#include "HidlUtils.h"
+
+using ::android::hardware::audio::effect::V2_0::Result;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Status;
+
+namespace android {
+
+EffectHalHidl::EffectHalHidl(const sp<IEffect>& effect, uint64_t effectId)
+        : mEffect(effect), mEffectId(effectId) {
+}
+
+EffectHalHidl::~EffectHalHidl() {
+}
+
+// static
+void EffectHalHidl::effectDescriptorToHal(
+        const EffectDescriptor& descriptor, effect_descriptor_t* halDescriptor) {
+    HidlUtils::uuidToHal(descriptor.type, &halDescriptor->type);
+    HidlUtils::uuidToHal(descriptor.uuid, &halDescriptor->uuid);
+    halDescriptor->flags = static_cast<uint32_t>(descriptor.flags);
+    halDescriptor->cpuLoad = descriptor.cpuLoad;
+    halDescriptor->memoryUsage = descriptor.memoryUsage;
+    memcpy(halDescriptor->name, descriptor.name.data(), descriptor.name.size());
+    memcpy(halDescriptor->implementor,
+            descriptor.implementor.data(), descriptor.implementor.size());
+}
+
+// static
+status_t EffectHalHidl::analyzeResult(const Result& result) {
+    switch (result) {
+        case Result::OK: return OK;
+        case Result::INVALID_ARGUMENTS: return BAD_VALUE;
+        case Result::INVALID_STATE: return NOT_ENOUGH_DATA;
+        case Result::NOT_INITIALIZED: return NO_INIT;
+        case Result::NOT_SUPPORTED: return INVALID_OPERATION;
+        case Result::RESULT_TOO_BIG: return NO_MEMORY;
+        default: return NO_INIT;
+    }
+}
+
+status_t EffectHalHidl::process(audio_buffer_t */*inBuffer*/, audio_buffer_t */*outBuffer*/) {
+    // Idea -- intercept set buffer config command, capture audio format, use it
+    // for determining frame size in bytes on input and output.
+    return OK;
+}
+
+status_t EffectHalHidl::processReverse(audio_buffer_t */*inBuffer*/, audio_buffer_t */*outBuffer*/) {
+    return OK;
+}
+
+status_t EffectHalHidl::command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData,
+        uint32_t *replySize, void *pReplyData) {
+    if (mEffect == 0) return NO_INIT;
+    hidl_vec<uint8_t> hidlData;
+    hidlData.setToExternal(reinterpret_cast<uint8_t*>(pCmdData), cmdSize);
+    status_t status;
+    Return<void> ret = mEffect->command(cmdCode, hidlData, *replySize,
+            [&](int32_t s, const hidl_vec<uint8_t>& result) {
+                status = s;
+                if (status == 0) {
+                    if (*replySize > result.size()) *replySize = result.size();
+                    if (pReplyData && *replySize > 0) {
+                        memcpy(pReplyData, &result[0], *replySize);
+                    }
+                }
+            });
+    return status;
+}
+
+status_t EffectHalHidl::getDescriptor(effect_descriptor_t *pDescriptor) {
+    if (mEffect == 0) return NO_INIT;
+    Result retval = Result::NOT_INITIALIZED;
+    Return<void> ret = mEffect->getDescriptor(
+            [&](Result r, const EffectDescriptor& result) {
+                retval = r;
+                if (retval == Result::OK) {
+                    effectDescriptorToHal(result, pDescriptor);
+                }
+            });
+    return ret.getStatus().isOk() ? analyzeResult(retval) : ret.getStatus().transactionError();
+}
+
+} // namespace android
diff --git a/media/libaudiohal/EffectHalHidl.h b/media/libaudiohal/EffectHalHidl.h
new file mode 100644
index 0000000..b79bee0
--- /dev/null
+++ b/media/libaudiohal/EffectHalHidl.h
@@ -0,0 +1,69 @@
+/*
+ * 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_EFFECT_HAL_HIDL_H
+#define ANDROID_HARDWARE_EFFECT_HAL_HIDL_H
+
+#include <android/hardware/audio/effect/2.0/IEffect.h>
+#include <media/audiohal/EffectHalInterface.h>
+#include <system/audio_effect.h>
+
+using ::android::hardware::audio::effect::V2_0::EffectDescriptor;
+using ::android::hardware::audio::effect::V2_0::IEffect;
+
+namespace android {
+
+class EffectHalHidl : public EffectHalInterface
+{
+  public:
+    // Effect process function. Takes input samples as specified
+    // in input buffer descriptor and output processed samples as specified
+    // in output buffer descriptor.
+    virtual status_t process(audio_buffer_t *inBuffer, audio_buffer_t *outBuffer);
+
+    // Process reverse stream function. This function is used to pass
+    // a reference stream to the effect engine.
+    virtual status_t processReverse(audio_buffer_t *inBuffer, audio_buffer_t *outBuffer);
+
+    // Send a command and receive a response to/from effect engine.
+    virtual status_t command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData,
+            uint32_t *replySize, void *pReplyData);
+
+    // Returns the effect descriptor.
+    virtual status_t getDescriptor(effect_descriptor_t *pDescriptor);
+
+    uint64_t effectId() const { return mEffectId; }
+
+    static void effectDescriptorToHal(
+            const EffectDescriptor& descriptor, effect_descriptor_t* halDescriptor);
+
+  private:
+    friend class EffectsFactoryHalHidl;
+    sp<IEffect> mEffect;
+    const uint64_t mEffectId;
+
+    static status_t analyzeResult(const hardware::audio::effect::V2_0::Result& result);
+
+    // Can not be constructed directly by clients.
+    EffectHalHidl(const sp<IEffect>& effect, uint64_t effectId);
+
+    // The destructor automatically releases the effect.
+    virtual ~EffectHalHidl();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_EFFECT_HAL_HIDL_H
diff --git a/media/libaudiohal/EffectsFactoryHalHidl.cpp b/media/libaudiohal/EffectsFactoryHalHidl.cpp
new file mode 100644
index 0000000..4f2eef0
--- /dev/null
+++ b/media/libaudiohal/EffectsFactoryHalHidl.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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 "EffectsFactoryHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <cutils/native_handle.h>
+#include <media/EffectsFactoryApi.h>
+
+#include "EffectHalHidl.h"
+#include "EffectsFactoryHalHidl.h"
+#include "HidlUtils.h"
+
+using ::android::hardware::audio::common::V2_0::Uuid;
+using ::android::hardware::audio::effect::V2_0::IEffect;
+using ::android::hardware::audio::effect::V2_0::Result;
+using ::android::hardware::Return;
+using ::android::hardware::Status;
+
+namespace android {
+
+// static
+sp<EffectsFactoryHalInterface> EffectsFactoryHalInterface::create() {
+    return new EffectsFactoryHalHidl();
+}
+
+// static
+bool EffectsFactoryHalInterface::isNullUuid(const effect_uuid_t *pEffectUuid) {
+    return EffectIsNullUuid(pEffectUuid);
+}
+
+EffectsFactoryHalHidl::EffectsFactoryHalHidl() {
+    mEffectsFactory = IEffectsFactory::getService("audio_effects_factory");
+}
+
+EffectsFactoryHalHidl::~EffectsFactoryHalHidl() {
+}
+
+status_t EffectsFactoryHalHidl::queryAllDescriptors() {
+    if (mEffectsFactory == 0) return NO_INIT;
+    Result retval = Result::NOT_INITIALIZED;
+    Return<void> ret = mEffectsFactory->getAllDescriptors(
+            [&](Result r, const hidl_vec<EffectDescriptor>& result) {
+                retval = r;
+                if (retval == Result::OK) {
+                    mLastDescriptors = result;
+                }
+            });
+    if (ret.getStatus().isOk()) {
+        return retval == Result::OK ? OK : NO_INIT;
+    }
+    mLastDescriptors.resize(0);
+    return ret.getStatus().transactionError();
+}
+
+status_t EffectsFactoryHalHidl::queryNumberEffects(uint32_t *pNumEffects) {
+    status_t queryResult = queryAllDescriptors();
+    if (queryResult == OK) {
+        *pNumEffects = mLastDescriptors.size();
+    }
+    return queryResult;
+}
+
+status_t EffectsFactoryHalHidl::getDescriptor(
+        uint32_t index, effect_descriptor_t *pDescriptor) {
+    // TODO: We need somehow to track the changes on the server side
+    // or figure out how to convert everybody to query all the descriptors at once.
+    // TODO: check for nullptr
+    if (mLastDescriptors.size() == 0) {
+        status_t queryResult = queryAllDescriptors();
+        if (queryResult != OK) return queryResult;
+    }
+    if (index >= mLastDescriptors.size()) return NAME_NOT_FOUND;
+    EffectHalHidl::effectDescriptorToHal(mLastDescriptors[index], pDescriptor);
+    return OK;
+}
+
+status_t EffectsFactoryHalHidl::getDescriptor(
+        const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptor) {
+    // TODO: check for nullptr
+    if (mEffectsFactory == 0) return NO_INIT;
+    Uuid hidlUuid;
+    HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid);
+    Result retval = Result::NOT_INITIALIZED;
+    Return<void> ret = mEffectsFactory->getDescriptor(hidlUuid,
+            [&](Result r, const EffectDescriptor& result) {
+                retval = r;
+                if (retval == Result::OK) {
+                    EffectHalHidl::effectDescriptorToHal(result, pDescriptor);
+                }
+            });
+    if (ret.getStatus().isOk()) {
+        if (retval == Result::OK) return OK;
+        else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND;
+        else return NO_INIT;
+    }
+    return ret.getStatus().transactionError();
+}
+
+status_t EffectsFactoryHalHidl::createEffect(
+        const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId,
+        sp<EffectHalInterface> *effect) {
+    if (mEffectsFactory == 0) return NO_INIT;
+    Uuid hidlUuid;
+    HidlUtils::uuidFromHal(*pEffectUuid, &hidlUuid);
+    Result retval = Result::NOT_INITIALIZED;
+    Return<void> ret = mEffectsFactory->createEffect(
+            hidlUuid, sessionId, ioId,
+            [&](Result r, const sp<IEffect>& result, uint64_t effectId) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *effect = new EffectHalHidl(result, effectId);
+                }
+            });
+    if (ret.getStatus().isOk()) {
+        if (retval == Result::OK) return OK;
+        else if (retval == Result::INVALID_ARGUMENTS) return NAME_NOT_FOUND;
+        else return NO_INIT;
+    }
+    return ret.getStatus().transactionError();
+}
+
+status_t EffectsFactoryHalHidl::dumpEffects(int fd) {
+    if (mEffectsFactory == 0) return NO_INIT;
+    native_handle_t* hidlHandle = native_handle_create(1, 0);
+    hidlHandle->data[0] = fd;
+    Return<void> ret = mEffectsFactory->debugDump(hidlHandle);
+    native_handle_delete(hidlHandle);
+    return ret.getStatus().transactionError();
+}
+
+} // namespace android
diff --git a/media/libaudiohal/EffectsFactoryHalHidl.h b/media/libaudiohal/EffectsFactoryHalHidl.h
new file mode 100644
index 0000000..f16db17
--- /dev/null
+++ b/media/libaudiohal/EffectsFactoryHalHidl.h
@@ -0,0 +1,67 @@
+/*
+ * 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_EFFECTS_FACTORY_HAL_HIDL_H
+#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H
+
+#include <android/hardware/audio/effect/2.0/IEffectsFactory.h>
+#include <android/hardware/audio/effect/2.0/types.h>
+#include <media/audiohal/EffectsFactoryHalInterface.h>
+
+namespace android {
+
+using ::android::hardware::audio::effect::V2_0::EffectDescriptor;
+using ::android::hardware::audio::effect::V2_0::IEffectsFactory;
+using ::android::hardware::hidl_vec;
+
+class EffectsFactoryHalHidl : public EffectsFactoryHalInterface
+{
+  public:
+    // Returns the number of different effects in all loaded libraries.
+    virtual status_t queryNumberEffects(uint32_t *pNumEffects);
+
+    // Returns a descriptor of the next available effect.
+    virtual status_t getDescriptor(uint32_t index,
+            effect_descriptor_t *pDescriptor);
+
+    virtual status_t getDescriptor(const effect_uuid_t *pEffectUuid,
+            effect_descriptor_t *pDescriptor);
+
+    // Creates an effect engine of the specified type.
+    // To release the effect engine, it is necessary to release references
+    // to the returned effect object.
+    virtual status_t createEffect(const effect_uuid_t *pEffectUuid,
+            int32_t sessionId, int32_t ioId,
+            sp<EffectHalInterface> *effect);
+
+    virtual status_t dumpEffects(int fd);
+
+  private:
+    friend class EffectsFactoryHalInterface;
+
+    sp<IEffectsFactory> mEffectsFactory;
+    hidl_vec<EffectDescriptor> mLastDescriptors;
+
+    // Can not be constructed directly by clients.
+    EffectsFactoryHalHidl();
+    virtual ~EffectsFactoryHalHidl();
+
+    status_t queryAllDescriptors();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H
diff --git a/media/libaudiohal/StreamHalHidl.cpp b/media/libaudiohal/StreamHalHidl.cpp
new file mode 100644
index 0000000..9383a36
--- /dev/null
+++ b/media/libaudiohal/StreamHalHidl.cpp
@@ -0,0 +1,384 @@
+/*
+ * 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 "StreamHalHidl"
+//#define LOG_NDEBUG 0
+
+#include <android/hardware/audio/2.0/IStreamOutCallback.h>
+#include <utils/Log.h>
+
+#include "DeviceHalHidl.h"
+#include "EffectHalHidl.h"
+#include "StreamHalHidl.h"
+
+using ::android::hardware::audio::common::V2_0::AudioChannelMask;
+using ::android::hardware::audio::common::V2_0::AudioFormat;
+using ::android::hardware::audio::V2_0::AudioDrain;
+using ::android::hardware::audio::V2_0::IStreamOutCallback;
+using ::android::hardware::audio::V2_0::ParameterValue;
+using ::android::hardware::audio::V2_0::Result;
+using ::android::hardware::audio::V2_0::TimeSpec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+namespace android {
+
+StreamHalHidl::StreamHalHidl(IStream *stream)
+        : ConversionHelperHidl("Stream"), mStream(stream) {
+}
+
+StreamHalHidl::~StreamHalHidl() {
+    mStream = nullptr;
+}
+
+status_t StreamHalHidl::getSampleRate(uint32_t *rate) {
+    if (!mStream) return NO_INIT;
+    return processReturn("getSampleRate", mStream->getSampleRate(), rate);
+}
+
+status_t StreamHalHidl::getBufferSize(size_t *size) {
+    if (!mStream) return NO_INIT;
+    return processReturn("getBufferSize", mStream->getBufferSize(), size);
+}
+
+status_t StreamHalHidl::getChannelMask(audio_channel_mask_t *mask) {
+    if (!mStream) return NO_INIT;
+    return processReturn("getChannelMask", mStream->getChannelMask(), mask);
+}
+
+status_t StreamHalHidl::getFormat(audio_format_t *format) {
+    if (!mStream) return NO_INIT;
+    return processReturn("getFormat", mStream->getFormat(), format);
+}
+
+status_t StreamHalHidl::getAudioProperties(
+        uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) {
+    if (!mStream) return NO_INIT;
+    Return<void> ret = mStream->getAudioProperties(
+            [&](uint32_t sr, AudioChannelMask m, AudioFormat f) {
+                *sampleRate = sr;
+                *mask = static_cast<audio_channel_mask_t>(m);
+                *format = static_cast<audio_format_t>(f);
+            });
+    return processReturn("getAudioProperties", ret);
+}
+
+status_t StreamHalHidl::setParameters(const String8& kvPairs) {
+    if (!mStream) return NO_INIT;
+    hidl_vec<ParameterValue> hidlParams;
+    status_t status = parametersFromHal(kvPairs, &hidlParams);
+    if (status != OK) return status;
+    return processReturn("setParameters", mStream->setParameters(hidlParams));
+}
+
+status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) {
+    values->clear();
+    if (!mStream) return NO_INIT;
+    hidl_vec<hidl_string> hidlKeys;
+    status_t status = keysFromHal(keys, &hidlKeys);
+    if (status != OK) return status;
+    Result retval;
+    Return<void> ret = mStream->getParameters(
+            hidlKeys,
+            [&](Result r, const hidl_vec<ParameterValue>& parameters) {
+                retval = r;
+                if (retval == Result::OK) {
+                    parametersToHal(parameters, values);
+                }
+            });
+    return processReturn("getParameters", ret, retval);
+}
+
+status_t StreamHalHidl::addEffect(sp<EffectHalInterface> effect) {
+    if (!mStream) return NO_INIT;
+    return processReturn("addEffect", mStream->addEffect(
+                    static_cast<EffectHalHidl*>(effect.get())->effectId()));
+}
+
+status_t StreamHalHidl::removeEffect(sp<EffectHalInterface> effect) {
+    if (!mStream) return NO_INIT;
+    return processReturn("removeEffect", mStream->removeEffect(
+                    static_cast<EffectHalHidl*>(effect.get())->effectId()));
+}
+
+status_t StreamHalHidl::standby() {
+    if (!mStream) return NO_INIT;
+    return processReturn("standby", mStream->standby());
+}
+
+status_t StreamHalHidl::dump(int fd) {
+    if (!mStream) return NO_INIT;
+    native_handle_t* hidlHandle = native_handle_create(1, 0);
+    hidlHandle->data[0] = fd;
+    Return<void> ret = mStream->debugDump(hidlHandle);
+    native_handle_delete(hidlHandle);
+    return processReturn("dump", ret);
+}
+
+
+namespace {
+
+/* Notes on callback ownership.
+
+This is how (Hw)Binder ownership model looks like. The server implementation
+is owned by Binder framework (via sp<>). Proxies are owned by clients.
+When the last proxy disappears, Binder framework releases the server impl.
+
+Thus, it is not needed to keep any references to StreamOutCallback (this is
+the server impl) -- it will live as long as HAL server holds a strong ref to
+IStreamOutCallback proxy. We clear that reference by calling 'clearCallback'
+from the destructor of StreamOutHalHidl.
+
+The callback only keeps a weak reference to the stream. The stream is owned
+by AudioFlinger.
+
+*/
+
+struct StreamOutCallback : public IStreamOutCallback {
+    StreamOutCallback(const wp<StreamOutHalHidl>& stream) : mStream(stream) {}
+
+    // IStreamOutCallback implementation
+    Return<void> onWriteReady()  override {
+        sp<StreamOutHalHidl> stream = mStream.promote();
+        if (stream != 0) {
+            stream->onWriteReady();
+        }
+        return Void();
+    }
+
+    Return<void> onDrainReady()  override {
+        sp<StreamOutHalHidl> stream = mStream.promote();
+        if (stream != 0) {
+            stream->onDrainReady();
+        }
+        return Void();
+    }
+
+    Return<void> onError()  override {
+        sp<StreamOutHalHidl> stream = mStream.promote();
+        if (stream != 0) {
+            stream->onError();
+        }
+        return Void();
+    }
+
+  private:
+    wp<StreamOutHalHidl> mStream;
+};
+
+}  // namespace
+
+StreamOutHalHidl::StreamOutHalHidl(const sp<IStreamOut>& stream)
+        : StreamHalHidl(stream.get()), mStream(stream) {
+}
+
+StreamOutHalHidl::~StreamOutHalHidl() {
+    if (mCallback.unsafe_get() && mStream != 0) {
+        processReturn("clearCallback", mStream->clearCallback());
+    }
+    mCallback.clear();
+}
+
+status_t StreamOutHalHidl::getFrameSize(size_t *size) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn("getFrameSize", mStream->getFrameSize(), size);
+}
+
+status_t StreamOutHalHidl::getLatency(uint32_t *latency) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn("getLatency", mStream->getLatency(), latency);
+}
+
+status_t StreamOutHalHidl::setVolume(float left, float right) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn("setVolume", mStream->setVolume(left, right));
+}
+
+status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) {
+    if (mStream == 0) return NO_INIT;
+    hidl_vec<uint8_t> hidlData;
+    hidlData.setToExternal(static_cast<uint8_t*>(const_cast<void*>(buffer)), bytes);
+    Result retval;
+    Return<void> ret = mStream->write(
+            hidlData,
+            [&](Result r, uint64_t w) {
+                retval = r;
+                *written = w;
+            });
+    return processReturn("write", ret, retval);
+}
+
+status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) {
+    if (mStream == 0) return NO_INIT;
+    Result retval;
+    Return<void> ret = mStream->getRenderPosition(
+            [&](Result r, uint32_t d) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *dspFrames = d;
+                }
+            });
+    return processReturn("getRenderPosition", ret, retval);
+}
+
+status_t StreamOutHalHidl::getNextWriteTimestamp(int64_t *timestamp) {
+    if (mStream == 0) return NO_INIT;
+    Result retval;
+    Return<void> ret = mStream->getNextWriteTimestamp(
+            [&](Result r, int64_t t) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *timestamp = t;
+                }
+            });
+    return processReturn("getRenderPosition", ret, retval);
+}
+
+status_t StreamOutHalHidl::setCallback(wp<StreamOutHalInterfaceCallback> callback) {
+    if (mStream == 0) return NO_INIT;
+    status_t status = processReturn(
+            "setCallback", mStream->setCallback(new StreamOutCallback(this)));
+    if (status == OK) {
+        mCallback = callback;
+    }
+    return status;
+}
+
+status_t StreamOutHalHidl::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) {
+    if (mStream == 0) return NO_INIT;
+    Return<void> ret = mStream->supportsPauseAndResume(
+            [&](bool p, bool r) {
+                *supportsPause = p;
+                *supportsResume = r;
+            });
+    return processReturn("supportsPauseAndResume", ret);
+}
+
+status_t StreamOutHalHidl::pause() {
+    if (mStream == 0) return NO_INIT;
+    return processReturn("pause", mStream->pause());
+}
+
+status_t StreamOutHalHidl::resume() {
+    if (mStream == 0) return NO_INIT;
+    return processReturn("pause", mStream->resume());
+}
+
+status_t StreamOutHalHidl::supportsDrain(bool *supportsDrain) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn("supportsDrain", mStream->supportsDrain(), supportsDrain);
+}
+
+status_t StreamOutHalHidl::drain(bool earlyNotify) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn(
+            "drain", mStream->drain(earlyNotify ? AudioDrain::EARLY_NOTIFY : AudioDrain::ALL));
+}
+
+status_t StreamOutHalHidl::flush() {
+    if (mStream == 0) return NO_INIT;
+    return processReturn("pause", mStream->flush());
+}
+
+status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) {
+    if (mStream == 0) return NO_INIT;
+    Result retval;
+    Return<void> ret = mStream->getPresentationPosition(
+            [&](Result r, uint64_t hidlFrames, const TimeSpec& hidlTimeStamp) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *frames = hidlFrames;
+                    timestamp->tv_sec = hidlTimeStamp.tvSec;
+                    timestamp->tv_nsec = hidlTimeStamp.tvNSec;
+                }
+            });
+    return processReturn("getPresentationPosition", ret, retval);
+}
+
+void StreamOutHalHidl::onWriteReady() {
+    sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+    if (callback == 0) return;
+    ALOGV("asyncCallback onWriteReady");
+    callback->onWriteReady();
+}
+
+void StreamOutHalHidl::onDrainReady() {
+    sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+    if (callback == 0) return;
+    ALOGV("asyncCallback onDrainReady");
+    callback->onDrainReady();
+}
+
+void StreamOutHalHidl::onError() {
+    sp<StreamOutHalInterfaceCallback> callback = mCallback.promote();
+    if (callback == 0) return;
+    ALOGV("asyncCallback onError");
+    callback->onError();
+}
+
+
+StreamInHalHidl::StreamInHalHidl(const sp<IStreamIn>& stream)
+        : StreamHalHidl(stream.get()), mStream(stream) {
+}
+
+StreamInHalHidl::~StreamInHalHidl() {
+}
+
+status_t StreamInHalHidl::getFrameSize(size_t *size) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn("getFrameSize", mStream->getFrameSize(), size);
+}
+
+status_t StreamInHalHidl::setGain(float gain) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn("setGain", mStream->setGain(gain));
+}
+
+status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) {
+    if (mStream == 0) return NO_INIT;
+    Result retval;
+    Return<void> ret = mStream->read(
+            bytes,
+            [&](Result r, const hidl_vec<uint8_t>& hidlData) {
+                retval = r;
+                *read = std::min(hidlData.size(), bytes);
+                if (retval == Result::OK) {
+                    memcpy(buffer, &hidlData[0], *read);
+                }
+            });
+    return processReturn("read", ret, retval);
+}
+
+status_t StreamInHalHidl::getInputFramesLost(uint32_t *framesLost) {
+    if (mStream == 0) return NO_INIT;
+    return processReturn("getInputFramesLost", mStream->getInputFramesLost(), framesLost);
+}
+
+status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) {
+    if (mStream == 0) return NO_INIT;
+    Result retval;
+    Return<void> ret = mStream->getCapturePosition(
+            [&](Result r, uint64_t hidlFrames, uint64_t hidlTime) {
+                retval = r;
+                if (retval == Result::OK) {
+                    *frames = hidlFrames;
+                    *time = hidlTime;
+                }
+            });
+    return processReturn("getCapturePosition", ret, retval);
+}
+
+} // namespace android
diff --git a/media/libaudiohal/StreamHalHidl.h b/media/libaudiohal/StreamHalHidl.h
new file mode 100644
index 0000000..e0a067e
--- /dev/null
+++ b/media/libaudiohal/StreamHalHidl.h
@@ -0,0 +1,174 @@
+/*
+ * 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_STREAM_HAL_HIDL_H
+#define ANDROID_HARDWARE_STREAM_HAL_HIDL_H
+
+#include <android/hardware/audio/2.0/IStream.h>
+#include <android/hardware/audio/2.0/IStreamIn.h>
+#include <android/hardware/audio/2.0/IStreamOut.h>
+#include <media/audiohal/StreamHalInterface.h>
+
+#include "ConversionHelperHidl.h"
+
+using ::android::hardware::audio::V2_0::IStream;
+using ::android::hardware::audio::V2_0::IStreamIn;
+using ::android::hardware::audio::V2_0::IStreamOut;
+using ::android::hardware::Return;
+
+namespace android {
+
+class DeviceHalHidl;
+
+class StreamHalHidl : public virtual StreamHalInterface, public ConversionHelperHidl
+{
+  public:
+    // Return the sampling rate in Hz - eg. 44100.
+    virtual status_t getSampleRate(uint32_t *rate);
+
+    // Return size of input/output buffer in bytes for this stream - eg. 4800.
+    virtual status_t getBufferSize(size_t *size);
+
+    // Return the channel mask.
+    virtual status_t getChannelMask(audio_channel_mask_t *mask);
+
+    // Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT.
+    virtual status_t getFormat(audio_format_t *format);
+
+    // Convenience method.
+    virtual status_t getAudioProperties(
+            uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format);
+
+    // Set audio stream parameters.
+    virtual status_t setParameters(const String8& kvPairs);
+
+    // Get audio stream parameters.
+    virtual status_t getParameters(const String8& keys, String8 *values);
+
+    // Add or remove the effect on the stream.
+    virtual status_t addEffect(sp<EffectHalInterface> effect);
+    virtual status_t removeEffect(sp<EffectHalInterface> effect);
+
+    // Put the audio hardware input/output into standby mode.
+    virtual status_t standby();
+
+    virtual status_t dump(int fd);
+
+  protected:
+    // Subclasses can not be constructed directly by clients.
+    explicit StreamHalHidl(IStream *stream);
+
+    // The destructor automatically closes the stream.
+    virtual ~StreamHalHidl();
+
+  private:
+    IStream *mStream;
+};
+
+class StreamOutHalHidl : public StreamOutHalInterface, public StreamHalHidl {
+  public:
+    // Return the frame size (number of bytes per sample) of a stream.
+    virtual status_t getFrameSize(size_t *size);
+
+    // Return the audio hardware driver estimated latency in milliseconds.
+    virtual status_t getLatency(uint32_t *latency);
+
+    // Use this method in situations where audio mixing is done in the hardware.
+    virtual status_t setVolume(float left, float right);
+
+    // Write audio buffer to driver.
+    virtual status_t write(const void *buffer, size_t bytes, size_t *written);
+
+    // Return the number of audio frames written by the audio dsp to DAC since
+    // the output has exited standby.
+    virtual status_t getRenderPosition(uint32_t *dspFrames);
+
+    // Get the local time at which the next write to the audio driver will be presented.
+    virtual status_t getNextWriteTimestamp(int64_t *timestamp);
+
+    // Set the callback for notifying completion of non-blocking write and drain.
+    virtual status_t setCallback(wp<StreamOutHalInterfaceCallback> callback);
+
+    // Returns whether pause and resume operations are supported.
+    virtual status_t supportsPauseAndResume(bool *supportsPause, bool *supportsResume);
+
+    // Notifies to the audio driver to resume playback following a pause.
+    virtual status_t pause();
+
+    // Notifies to the audio driver to resume playback following a pause.
+    virtual status_t resume();
+
+    // Returns whether drain operation is supported.
+    virtual status_t supportsDrain(bool *supportsDrain);
+
+    // Requests notification when data buffered by the driver/hardware has been played.
+    virtual status_t drain(bool earlyNotify);
+
+    // Notifies to the audio driver to flush the queued data.
+    virtual status_t flush();
+
+    // Return a recent count of the number of audio frames presented to an external observer.
+    virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp);
+
+    // Methods used by StreamOutCallback (HIDL).
+    void onWriteReady();
+    void onDrainReady();
+    void onError();
+
+  private:
+    friend class DeviceHalHidl;
+
+    wp<StreamOutHalInterfaceCallback> mCallback;
+    sp<IStreamOut> mStream;
+
+    // Can not be constructed directly by clients.
+    StreamOutHalHidl(const sp<IStreamOut>& stream);
+
+    virtual ~StreamOutHalHidl();
+};
+
+class StreamInHalHidl : public StreamInHalInterface, public StreamHalHidl {
+  public:
+    // Return the frame size (number of bytes per sample) of a stream.
+    virtual status_t getFrameSize(size_t *size);
+
+    // Set the input gain for the audio driver.
+    virtual status_t setGain(float gain);
+
+    // Read audio buffer in from driver.
+    virtual status_t read(void *buffer, size_t bytes, size_t *read);
+
+    // Return the amount of input frames lost in the audio driver.
+    virtual status_t getInputFramesLost(uint32_t *framesLost);
+
+    // Return a recent count of the number of audio frames received and
+    // the clock time associated with that frame count.
+    virtual status_t getCapturePosition(int64_t *frames, int64_t *time);
+
+  private:
+    friend class DeviceHalHidl;
+
+    sp<IStreamIn> mStream;
+
+    // Can not be constructed directly by clients.
+    StreamInHalHidl(const sp<IStreamIn>& stream);
+
+    virtual ~StreamInHalHidl();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_STREAM_HAL_HIDL_H
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index f4d8bc0..02947b0 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -26,6 +26,7 @@
     IMediaPlayer.cpp \
     IMediaRecorder.cpp \
     IMediaSource.cpp \
+    IMediaAnalyticsService.cpp \
     IRemoteDisplay.cpp \
     IRemoteDisplayClient.cpp \
     IResourceManagerClient.cpp \
@@ -34,6 +35,7 @@
     MediaCodecBuffer.cpp \
     MediaCodecInfo.cpp \
     MediaDefs.cpp \
+    MediaAnalyticsItem.cpp \
     MediaUtils.cpp \
     Metadata.cpp \
     mediarecorder.cpp \
@@ -59,7 +61,7 @@
         libcamera_client libstagefright_foundation \
         libgui libdl libaudioutils libaudioclient
 
-LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder libsonivox
 
 LOCAL_WHOLE_STATIC_LIBRARIES := libmedia_helper
 
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
index d244a0a..65fc70b 100644
--- a/media/libmedia/AudioParameter.cpp
+++ b/media/libmedia/AudioParameter.cpp
@@ -189,6 +189,16 @@
     return result;
 }
 
+status_t AudioParameter::getAt(size_t index, String8& key) const
+{
+    if (mParameters.size() > index) {
+        key = mParameters.keyAt(index);
+        return NO_ERROR;
+    } else {
+        return BAD_VALUE;
+    }
+}
+
 status_t AudioParameter::getAt(size_t index, String8& key, String8& value) const
 {
     if (mParameters.size() > index) {
diff --git a/media/libmedia/IMediaAnalyticsService.cpp b/media/libmedia/IMediaAnalyticsService.cpp
new file mode 100644
index 0000000..afe9c36
--- /dev/null
+++ b/media/libmedia/IMediaAnalyticsService.cpp
@@ -0,0 +1,241 @@
+/*
+ * 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 "MediaAnalytics"
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <binder/IPCThreadState.h>
+#include <media/IHDCP.h>
+#include <media/IMediaCodecList.h>
+#include <media/IMediaHTTPService.h>
+#include <media/IMediaPlayerService.h>
+#include <media/IMediaRecorder.h>
+#include <media/IOMX.h>
+#include <media/IRemoteDisplay.h>
+#include <media/IRemoteDisplayClient.h>
+#include <media/IStreamSource.h>
+
+#include <utils/Errors.h>  // for status_t
+#include <utils/List.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <media/MediaAnalyticsItem.h>
+#include <media/IMediaAnalyticsService.h>
+
+#define DEBUGGING               0
+#define DEBUGGING_FLOW          0
+#define DEBUGGING_RETURNS       0
+
+namespace android {
+
+enum {
+    GENERATE_UNIQUE_SESSIONID = IBinder::FIRST_CALL_TRANSACTION,
+    SUBMIT_ITEM,
+    GET_ITEM_LIST,
+};
+
+class BpMediaAnalyticsService: public BpInterface<IMediaAnalyticsService>
+{
+public:
+    explicit BpMediaAnalyticsService(const sp<IBinder>& impl)
+        : BpInterface<IMediaAnalyticsService>(impl)
+    {
+    }
+
+    virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() {
+        Parcel data, reply;
+        status_t err;
+        MediaAnalyticsItem::SessionID_t sessionid =
+                        MediaAnalyticsItem::SessionIDInvalid;
+
+        data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
+        err = remote()->transact(GENERATE_UNIQUE_SESSIONID, data, &reply);
+        if (err != NO_ERROR) {
+            ALOGW("bad response from service");
+            return MediaAnalyticsItem::SessionIDInvalid;
+        }
+        sessionid = reply.readInt64();
+        if (DEBUGGING_RETURNS) {
+            ALOGD("the caller gets a sessionid of %" PRId64 " back", sessionid);
+        }
+        return sessionid;
+    }
+
+    virtual MediaAnalyticsItem::SessionID_t submit(sp<MediaAnalyticsItem> item, bool forcenew)
+    {
+        // have this record submit itself
+        // this will be a binder call with appropriate timing
+        // return value is the uuid that the system generated for it.
+        // the return value 0 and -1 are reserved.
+        // -1 to indicate that there was a problem recording...
+
+        Parcel data, reply;
+        status_t err;
+
+        if (item == NULL) {
+                return MediaAnalyticsItem::SessionIDInvalid;
+        }
+
+        data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
+        if(DEBUGGING_FLOW) {
+            ALOGD("client offers record: %s", item->toString().c_str());
+        }
+        data.writeBool(forcenew);
+        item->writeToParcel(&data);
+
+        err = remote()->transact(SUBMIT_ITEM, data, &reply);
+        if (err != NO_ERROR) {
+            return MediaAnalyticsItem::SessionIDInvalid;
+        }
+
+        // get an answer out of 'reply'
+        int64_t sessionid = reply.readInt64();
+        if (DEBUGGING_RETURNS) {
+            ALOGD("the caller gets sessionid=%" PRId64 "", sessionid);
+        }
+        return sessionid;
+    }
+
+    virtual List<sp<MediaAnalyticsItem>> *getMediaAnalyticsItemList(bool finished, nsecs_t ts)
+    {
+            return getMediaAnalyticsItemList(finished, ts, MediaAnalyticsItem::kKeyAny);
+    }
+
+    virtual List<sp<MediaAnalyticsItem>> *getMediaAnalyticsItemList(bool finished, nsecs_t ts, MediaAnalyticsItem::Key key)
+    {
+        Parcel data, reply;
+        status_t err;
+
+        data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
+        data.writeInt32(finished);
+        data.writeInt64(ts);
+        const char *str = key.c_str();
+        if (key.empty()) {
+            str = MediaAnalyticsItem::kKeyNone.c_str();
+        }
+        data.writeCString(str);
+        err = remote()->transact(GET_ITEM_LIST, data, &reply);
+	if (err != NO_ERROR) {
+	    return NULL;
+	}
+
+        // read a count
+        int32_t count = reply.readInt32();
+        List<sp<MediaAnalyticsItem>> *list = NULL;
+
+        if (count > 0) {
+            list = new List<sp<MediaAnalyticsItem>>();
+            for (int i=0;i<count;i++) {
+                sp<MediaAnalyticsItem> item = new MediaAnalyticsItem;
+                // XXX: watch for failures here
+                item->readFromParcel(reply);
+                list->push_back(item);
+            }
+        }
+
+        return list;
+    }
+};
+
+IMPLEMENT_META_INTERFACE(MediaAnalyticsService, "android.media.IMediaAnalyticsService");
+
+// ----------------------------------------------------------------------
+
+status_t BnMediaAnalyticsService::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+
+
+    // get calling pid/tid
+    IPCThreadState *ipc = IPCThreadState::self();
+    int clientPid = ipc->getCallingPid();
+    // permission checking
+
+    if(DEBUGGING_FLOW) {
+        ALOGD("running in service, code %d, pid %d; called from pid %d",
+            code, getpid(), clientPid);
+    }
+
+    switch (code) {
+
+        case GENERATE_UNIQUE_SESSIONID: {
+            CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
+
+            MediaAnalyticsItem::SessionID_t sessionid = generateUniqueSessionID();
+            reply->writeInt64(sessionid);
+
+            return NO_ERROR;
+        } break;
+
+        case SUBMIT_ITEM: {
+            CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
+
+            bool forcenew;
+            sp<MediaAnalyticsItem> item = new MediaAnalyticsItem;
+
+            data.readBool(&forcenew);
+            item->readFromParcel(data);
+
+            item->setPid(clientPid);
+
+	    // submit() takes ownership of / responsibility for the item
+            MediaAnalyticsItem::SessionID_t sessionid = submit(item, forcenew);
+            reply->writeInt64(sessionid);
+
+            return NO_ERROR;
+        } break;
+
+        case GET_ITEM_LIST: {
+            CHECK_INTERFACE(IMediaPlayerService, data, reply);
+            // get the parameters
+            bool finished = data.readInt32();
+            nsecs_t ts = data.readInt64();
+            MediaAnalyticsItem::Key key = data.readCString();
+
+            // find the (0 or more) items
+            List<sp<MediaAnalyticsItem>> *list =  getMediaAnalyticsItemList(finished, ts, key);
+            // encapsulate/serialize them
+            reply->writeInt32(list->size());
+            if (list->size() > 0) {
+                    for (List<sp<MediaAnalyticsItem>>::iterator it = list->begin();
+                         it != list->end(); it++) {
+                            (*it)->writeToParcel(reply);
+                    }
+
+
+            }
+
+            // avoid leakiness; organized discarding of list and its contents
+            list->clear();
+            delete list;
+
+            return NO_ERROR;
+        } break;
+
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+} // namespace android
diff --git a/media/libmedia/MediaAnalyticsItem.cpp b/media/libmedia/MediaAnalyticsItem.cpp
new file mode 100644
index 0000000..5f05d5a
--- /dev/null
+++ b/media/libmedia/MediaAnalyticsItem.cpp
@@ -0,0 +1,703 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "MediaAnalyticsItem"
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include <binder/Parcel.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/threads.h>
+
+#include <media/stagefright/foundation/AString.h>
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaAnalyticsService.h>
+#include <media/MediaAnalyticsItem.h>
+
+namespace android {
+
+#define DEBUG_SERVICEACCESS     0
+
+// the few universal keys we have
+const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyAny  = "any";
+const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyNone  = "none";
+
+const char * const MediaAnalyticsItem::EnabledProperty  = "media.analytics.enabled";
+const char * const MediaAnalyticsItem::EnabledPropertyPersist  = "persist.media.analytics.enabled";
+const int MediaAnalyticsItem::EnabledProperty_default  = 0;
+
+
+// access functions for the class
+MediaAnalyticsItem::MediaAnalyticsItem()
+    : RefBase(),
+      mPid(0),
+      mUid(0),
+      mSessionID(MediaAnalyticsItem::SessionIDNone),
+      mTimestamp(0),
+      mFinalized(0) {
+    mKey = MediaAnalyticsItem::kKeyNone;
+}
+
+MediaAnalyticsItem::MediaAnalyticsItem(MediaAnalyticsItem::Key key)
+    : RefBase(),
+      mPid(0),
+      mUid(0),
+      mSessionID(MediaAnalyticsItem::SessionIDNone),
+      mTimestamp(0),
+      mFinalized(0) {
+    mKey = key;
+}
+
+MediaAnalyticsItem::~MediaAnalyticsItem() {
+    clear();
+}
+
+// so clients can send intermediate values to be overlaid later
+MediaAnalyticsItem &MediaAnalyticsItem::setFinalized(bool value) {
+    mFinalized = value;
+    return *this;
+}
+
+bool MediaAnalyticsItem::getFinalized() const {
+    return mFinalized;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
+    mSessionID = id;
+    return *this;
+}
+
+MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const {
+    return mSessionID;
+}
+
+MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::generateSessionID() {
+    MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
+    ALOGD("generateSessionID()");
+
+    if (mSessionID == SessionIDNone) {
+        // get one from the server
+        sp<IMediaAnalyticsService> svc = getInstance();
+        if (svc != NULL) {
+            newid = svc->generateUniqueSessionID();
+        }
+        mSessionID = newid;
+    }
+
+    return mSessionID;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() {
+    mSessionID = MediaAnalyticsItem::SessionIDNone;
+    return *this;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) {
+    mTimestamp = ts;
+    return *this;
+}
+
+nsecs_t MediaAnalyticsItem::getTimestamp() const {
+    return mTimestamp;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setPid(pid_t pid) {
+    mPid = pid;
+    return *this;
+}
+
+pid_t MediaAnalyticsItem::getPid() const {
+    return mPid;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setUid(uid_t uid) {
+    mUid = uid;
+    return *this;
+}
+
+uid_t MediaAnalyticsItem::getUid() const {
+    return mUid;
+}
+
+void MediaAnalyticsItem::clear() {
+
+    mKey.clear();
+
+#if 0
+    // not sure that I need to (or should) be doing this...
+    // seeing some strangeness in some records
+    int count = mItems.size();
+    for (int i = 0 ; i < count; i++ ) {
+        MediaAnalyticsItem::Attr attr = mItems.keyAt(i);
+        const sp<Item> value = mItems.valueAt(i);
+        value->clear();
+        attr.clear();
+    }
+    mItems.clear();
+#endif
+
+    return;
+}
+
+// this key is for the overall record -- "vid" or "aud"
+// assuming for the moment we use int32_t like the
+// media frameworks MetaData.cpp
+MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
+    // XXX: possible validation of legal keys.
+    mKey = key;
+    return *this;
+}
+
+MediaAnalyticsItem::Key MediaAnalyticsItem::getKey() {
+    return mKey;
+}
+
+// number of keys we have in our dictionary
+// we won't upload empty records
+int32_t MediaAnalyticsItem::count() const {
+    return mItems.size();
+}
+
+// set the values
+bool MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr attr, int32_t value) {
+    ssize_t i = mItems.indexOfKey(attr);
+    bool overwrote = true;
+    if (i<0) {
+        sp<Item> item = new Item();
+        i = mItems.add(attr, item);
+        overwrote = false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    item->mType = MediaAnalyticsItem::Item::kTypeInt32;
+    item->u.int32Value = value;
+    return overwrote;
+}
+
+bool MediaAnalyticsItem::setInt64(MediaAnalyticsItem::Attr attr, int64_t value) {
+    ssize_t i = mItems.indexOfKey(attr);
+    bool overwrote = true;
+    if (i<0) {
+        sp<Item> item = new Item();
+        i = mItems.add(attr, item);
+        overwrote = false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    item->mType = MediaAnalyticsItem::Item::kTypeInt64;
+    item->u.int64Value = value;
+    return overwrote;
+}
+
+bool MediaAnalyticsItem::setDouble(MediaAnalyticsItem::Attr attr, double value) {
+    ssize_t i = mItems.indexOfKey(attr);
+    bool overwrote = true;
+    if (i<0) {
+        sp<Item> item = new Item();
+        i = mItems.add(attr, item);
+        overwrote = false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    item->mType = MediaAnalyticsItem::Item::kTypeDouble;
+    item->u.doubleValue = value;
+    return overwrote;
+}
+
+bool MediaAnalyticsItem::setCString(MediaAnalyticsItem::Attr attr, const char *value) {
+    bool overwrote = true;
+    if (value == NULL) return false;
+    // we store our own copy of the supplied string
+    char *nvalue = strdup(value);
+    if (nvalue == NULL) {
+            return false;
+    }
+    ssize_t i = mItems.indexOfKey(attr);
+    if (i<0) {
+        sp<Item> item = new Item();
+        i = mItems.add(attr, item);
+        overwrote = false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    if (item->mType == MediaAnalyticsItem::Item::kTypeCString
+        && item->u.CStringValue != NULL) {
+            free(item->u.CStringValue);
+            item->u.CStringValue = NULL;
+    }
+    item->mType = MediaAnalyticsItem::Item::kTypeCString;
+    item->u.CStringValue = nvalue;
+    return true;
+}
+
+// find/add/set fused into a single operation
+bool MediaAnalyticsItem::addInt32(MediaAnalyticsItem::Attr attr, int32_t value) {
+    ssize_t i = mItems.indexOfKey(attr);
+    bool overwrote = true;
+    if (i<0) {
+        sp<Item> item = new Item();
+        i = mItems.add(attr, item);
+        overwrote = false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    if (overwrote
+        && item->mType == MediaAnalyticsItem::Item::kTypeInt32) {
+        item->u.int32Value += value;
+    } else {
+        // start clean if there was a type mismatch
+        item->u.int32Value = value;
+    }
+    item->mType = MediaAnalyticsItem::Item::kTypeInt32;
+    return overwrote;
+}
+
+bool MediaAnalyticsItem::addInt64(MediaAnalyticsItem::Attr attr, int64_t value) {
+    ssize_t i = mItems.indexOfKey(attr);
+    bool overwrote = true;
+    if (i<0) {
+        sp<Item> item = new Item();
+        i = mItems.add(attr, item);
+        overwrote = false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    if (overwrote
+        && item->mType == MediaAnalyticsItem::Item::kTypeInt64) {
+        item->u.int64Value += value;
+    } else {
+        // start clean if there was a type mismatch
+        item->u.int64Value = value;
+    }
+    item->mType = MediaAnalyticsItem::Item::kTypeInt64;
+    return overwrote;
+}
+
+bool MediaAnalyticsItem::addDouble(MediaAnalyticsItem::Attr attr, double value) {
+    ssize_t i = mItems.indexOfKey(attr);
+    bool overwrote = true;
+    if (i<0) {
+        sp<Item> item = new Item();
+        i = mItems.add(attr, item);
+        overwrote = false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    if (overwrote
+        && item->mType == MediaAnalyticsItem::Item::kTypeDouble) {
+        item->u.doubleValue += value;
+    } else {
+        // start clean if there was a type mismatch
+        item->u.doubleValue = value;
+    }
+    item->mType = MediaAnalyticsItem::Item::kTypeDouble;
+    return overwrote;
+}
+
+// find & extract values
+bool MediaAnalyticsItem::getInt32(MediaAnalyticsItem::Attr attr, int32_t *value) {
+    ssize_t i = mItems.indexOfKey(attr);
+    if (i < 0) {
+        return false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    *value = item->u.int32Value;
+    return true;
+}
+bool MediaAnalyticsItem::getInt64(MediaAnalyticsItem::Attr attr, int64_t *value) {
+    ssize_t i = mItems.indexOfKey(attr);
+    if (i < 0) {
+        return false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    *value = item->u.int64Value;
+    return true;
+}
+bool MediaAnalyticsItem::getDouble(MediaAnalyticsItem::Attr attr, double *value) {
+    ssize_t i = mItems.indexOfKey(attr);
+    if (i < 0) {
+        return false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    *value = item->u.doubleValue;
+    return true;
+}
+
+// caller responsible for the returned string
+bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr attr, char **value) {
+    ssize_t i = mItems.indexOfKey(attr);
+    if (i < 0) {
+        return false;
+    }
+    sp<Item> &item = mItems.editValueAt(i);
+    char *p = strdup(item->u.CStringValue);
+    *value = p;
+    return true;
+}
+
+// remove indicated keys and their values
+// return value is # keys removed
+int32_t MediaAnalyticsItem::filter(int n, MediaAnalyticsItem::Attr attrs[]) {
+    int zapped = 0;
+    if (attrs == NULL) {
+        return -1;
+    }
+    if (n <= 0) {
+        return -1;
+    }
+    for (ssize_t i = 0 ; i < n ;  i++) {
+        ssize_t j = mItems.indexOfKey(attrs[i]);
+        if (j >= 0) {
+            mItems.removeItemsAt(j);
+            zapped++;
+        }
+    }
+    return zapped;
+}
+
+// remove any keys NOT in the provided list
+// return value is # keys removed
+int32_t MediaAnalyticsItem::filterNot(int n, MediaAnalyticsItem::Attr attrs[]) {
+    int zapped = 0;
+    if (attrs == NULL) {
+        return -1;
+    }
+    if (n <= 0) {
+        return -1;
+    }
+    for (ssize_t i = mItems.size()-1 ; i >=0 ;  i--) {
+        const MediaAnalyticsItem::Attr& lattr = mItems.keyAt(i);
+        ssize_t j;
+        for (j= 0; j < n ; j++) {
+            if (lattr == attrs[j]) {
+                mItems.removeItemsAt(i);
+                zapped++;
+                break;
+            }
+        }
+    }
+    return zapped;
+}
+
+// remove a single key
+// return value is 0 (not found) or 1 (found and removed)
+int32_t MediaAnalyticsItem::filter(MediaAnalyticsItem::Attr attr) {
+    if (attr == 0) return -1;
+    ssize_t i = mItems.indexOfKey(attr);
+    if (i < 0) {
+        return 0;
+    }
+    mItems.removeItemsAt(i);
+    return 1;
+}
+
+
+// handle individual items/properties stored within the class
+//
+MediaAnalyticsItem::Item::Item()
+        : mType(kTypeNone)
+{
+}
+
+MediaAnalyticsItem::Item::~Item()
+{
+    clear();
+}
+
+void MediaAnalyticsItem::Item::clear()
+{
+    if (mType == kTypeCString && u.CStringValue != NULL) {
+        free(u.CStringValue);
+        u.CStringValue = NULL;
+    }
+    mType = kTypeNone;
+}
+
+// Parcel / serialize things for binder calls
+//
+
+int32_t MediaAnalyticsItem::readFromParcel(const Parcel& data) {
+    // into 'this' object
+    // .. we make a copy of the string to put away.
+    mKey = data.readCString();
+    mSessionID = data.readInt64();
+    mFinalized = data.readInt32();
+    mTimestamp = data.readInt64();
+
+    int count = data.readInt32();
+    for (int i = 0; i < count ; i++) {
+            MediaAnalyticsItem::Attr attr = data.readCString();
+            int32_t ztype = data.readInt32();
+                switch (ztype) {
+                    case MediaAnalyticsItem::Item::kTypeInt32:
+                            setInt32(attr, data.readInt32());
+                            break;
+                    case MediaAnalyticsItem::Item::kTypeInt64:
+                            setInt64(attr, data.readInt64());
+                            break;
+                    case MediaAnalyticsItem::Item::kTypeDouble:
+                            setDouble(attr, data.readDouble());
+                            break;
+                    case MediaAnalyticsItem::Item::kTypeCString:
+                            setCString(attr, data.readCString());
+                            break;
+                    default:
+                            ALOGE("reading bad item type: %d, idx %d",
+                                  ztype, i);
+                            return -1;
+                }
+    }
+
+    return 0;
+}
+
+int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) {
+    if (data == NULL) return -1;
+
+
+    data->writeCString(mKey.c_str());
+    data->writeInt64(mSessionID);
+    data->writeInt32(mFinalized);
+    data->writeInt64(mTimestamp);
+
+    // set of items
+    int count = mItems.size();
+    data->writeInt32(count);
+    for (int i = 0 ; i < count; i++ ) {
+            MediaAnalyticsItem::Attr attr = mItems.keyAt(i);
+            sp<Item> value = mItems.valueAt(i);
+            {
+                data->writeCString(attr.c_str());
+                data->writeInt32(value->mType);
+                switch (value->mType) {
+                    case MediaAnalyticsItem::Item::kTypeInt32:
+                            data->writeInt32(value->u.int32Value);
+                            break;
+                    case MediaAnalyticsItem::Item::kTypeInt64:
+                            data->writeInt64(value->u.int64Value);
+                            break;
+                    case MediaAnalyticsItem::Item::kTypeDouble:
+                            data->writeDouble(value->u.doubleValue);
+                            break;
+                    case MediaAnalyticsItem::Item::kTypeCString:
+                            data->writeCString(value->u.CStringValue);
+                            break;
+                    default:
+                            ALOGE("found bad item type: %d, idx %d",
+                                  value->mType, i);
+                            break;
+                }
+            }
+    }
+
+    return 0;
+}
+
+
+
+AString MediaAnalyticsItem::toString() {
+
+    AString result = "(";
+    char buffer[256];
+
+    // same order as we spill into the parcel, although not required
+    // key+session are our primary matching criteria
+    result.append(mKey.c_str());
+    result.append(":");
+    snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
+    result.append(buffer);
+
+    // we need these internally, but don't want to upload them
+    snprintf(buffer, sizeof(buffer), "%d:%d", mUid, mPid);
+    result.append(buffer);
+
+    snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
+    result.append(buffer);
+    snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mTimestamp);
+    result.append(buffer);
+
+    // set of items
+    int count = mItems.size();
+    snprintf(buffer, sizeof(buffer), "%d:", count);
+    result.append(buffer);
+    for (int i = 0 ; i < count; i++ ) {
+            const MediaAnalyticsItem::Attr attr = mItems.keyAt(i);
+            const sp<Item> value = mItems.valueAt(i);
+            switch (value->mType) {
+                case MediaAnalyticsItem::Item::kTypeInt32:
+                        snprintf(buffer,sizeof(buffer),
+                        "%s=%d:", attr.c_str(), value->u.int32Value);
+                        break;
+                case MediaAnalyticsItem::Item::kTypeInt64:
+                        snprintf(buffer,sizeof(buffer),
+                        "%s=%" PRId64 ":", attr.c_str(), value->u.int64Value);
+                        break;
+                case MediaAnalyticsItem::Item::kTypeDouble:
+                        snprintf(buffer,sizeof(buffer),
+                        "%s=%e:", attr.c_str(), value->u.doubleValue);
+                        break;
+                case MediaAnalyticsItem::Item::kTypeCString:
+                        // XXX: worry about escape chars
+                        // XXX: worry about overflowing buffer
+                        snprintf(buffer,sizeof(buffer), "%s=", attr.c_str());
+                        result.append(buffer);
+                        result.append(value->u.CStringValue);
+                        buffer[0] = ':';
+                        buffer[1] = '\0';
+                        break;
+                default:
+                        ALOGE("to_String bad item type: %d",
+                              value->mType);
+                        break;
+            }
+            result.append(buffer);
+    }
+
+    result.append(")");
+
+    return result;
+}
+
+// for the lazy, we offer methods that finds the service and
+// calls the appropriate daemon
+bool MediaAnalyticsItem::selfrecord() {
+    return selfrecord(false);
+}
+
+bool MediaAnalyticsItem::selfrecord(bool forcenew) {
+
+    AString p = this->toString();
+    ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
+
+    sp<IMediaAnalyticsService> svc = getInstance();
+
+    if (svc != NULL) {
+        svc->submit(this, forcenew);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+// get a connection we can reuse for most of our lifetime
+// static
+sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
+static Mutex sInitMutex;
+
+//static
+bool MediaAnalyticsItem::isEnabled() {
+    int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
+
+    if (enabled == -1) {
+        enabled = property_get_int32(MediaAnalyticsItem::EnabledPropertyPersist, -1);
+    }
+    if (enabled == -1) {
+        enabled = MediaAnalyticsItem::EnabledProperty_default;
+    }
+    if (enabled <= 0) {
+        return false;
+    }
+    return true;
+}
+
+//static
+sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
+    static const char *servicename = "media.analytics";
+    int enabled = isEnabled();
+
+    if (enabled == false) {
+        if (DEBUG_SERVICEACCESS) {
+                ALOGD("disabled");
+        }
+        return NULL;
+    }
+
+    {
+        Mutex::Autolock _l(sInitMutex);
+        const char *badness = "";
+
+
+        if (sAnalyticsService == NULL) {
+            sp<IServiceManager> sm = defaultServiceManager();
+            if (sm != NULL) {
+                sp<IBinder> binder = sm->getService(String16(servicename));
+                if (binder != NULL) {
+                    sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
+                } else {
+                    badness = "did not find service";
+                }
+            } else {
+                badness = "No Service Manager access";
+            }
+            // always
+            if (1 || DEBUG_SERVICEACCESS) {
+                if (sAnalyticsService == NULL) {
+                    ALOGD("Unable to bind to service %s: %s", servicename, badness);
+                }
+            }
+        }
+        return sAnalyticsService;
+    }
+}
+
+
+// merge the info from 'incoming' into this record.
+// we finish with a union of this+incoming and special handling for collisions
+bool MediaAnalyticsItem::merge(sp<MediaAnalyticsItem> incoming) {
+
+    // if I don't have key or session id, take them from incoming
+    // 'this' should never be missing both of them...
+    if (mKey.empty()) {
+        mKey = incoming->mKey;
+    } else if (mSessionID == 0) {
+        mSessionID = incoming->mSessionID;
+    }
+
+    // we always take the more recent 'finalized' value
+    setFinalized(incoming->getFinalized());
+
+    // for each attribute from 'incoming', resolve appropriately
+    int nattr = incoming->mItems.size();
+    for (int i = 0 ; i < nattr; i++ ) {
+        const MediaAnalyticsItem::Attr attr = incoming->mItems.keyAt(i);
+        const sp<Item> value = incoming->mItems.valueAt(i);
+
+        const char *p = attr.c_str();
+        char semantic = p[strlen(p)-1];
+
+        switch (semantic) {
+            default:        // default operation is keep new
+            case '>':       // last aka keep new
+                mItems.replaceValueFor(attr, value);
+                break;
+
+            case '<':       /* first aka keep first*/
+                /* nop */
+                break;
+
+            case '+':       /* sum */
+                // XXX validate numeric types, sum in place
+                break;
+
+        }
+    }
+
+    // not sure when we'd return false...
+    return true;
+}
+
+} // namespace android
+
diff --git a/media/libmediaanalyticsservice/Android.mk b/media/libmediaanalyticsservice/Android.mk
new file mode 100644
index 0000000..dd59651
--- /dev/null
+++ b/media/libmediaanalyticsservice/Android.mk
@@ -0,0 +1,44 @@
+LOCAL_PATH:= $(call my-dir)
+
+#
+# libmediaanalyticsservice
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=               \
+    MediaAnalyticsService.cpp      \
+
+LOCAL_SHARED_LIBRARIES :=       \
+    libbinder                   \
+    libcutils                   \
+    liblog                      \
+    libdl                       \
+    libgui                      \
+    libmedia                    \
+    libmediautils               \
+    libstagefright_foundation   \
+    libutils
+
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libmedia
+
+LOCAL_C_INCLUDES :=                                                 \
+    $(TOP)/frameworks/av/media/libstagefright/include               \
+    $(TOP)/frameworks/av/media/libstagefright/rtsp                  \
+    $(TOP)/frameworks/av/media/libstagefright/wifi-display          \
+    $(TOP)/frameworks/av/media/libstagefright/webm                  \
+    $(TOP)/frameworks/av/include/media                              \
+    $(TOP)/frameworks/av/include/camera                             \
+    $(TOP)/frameworks/native/include/media/openmax                  \
+    $(TOP)/frameworks/native/include/media/hardware                 \
+    $(TOP)/external/tremolo/Tremolo                                 \
+    libcore/include                                                 \
+
+LOCAL_CFLAGS += -Werror -Wno-error=deprecated-declarations -Wall
+LOCAL_CLANG := true
+
+LOCAL_MODULE:= libmediaanalyticsservice
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libmediaanalyticsservice/MediaAnalyticsService.cpp b/media/libmediaanalyticsservice/MediaAnalyticsService.cpp
new file mode 100644
index 0000000..a039c6c
--- /dev/null
+++ b/media/libmediaanalyticsservice/MediaAnalyticsService.cpp
@@ -0,0 +1,459 @@
+/*
+ * 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.
+ */
+
+// Proxy for media player implementations
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaAnalyticsService"
+#include <utils/Log.h>
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#include <string.h>
+
+#include <cutils/atomic.h>
+#include <cutils/properties.h> // for property_get
+
+#include <utils/misc.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+#include <gui/Surface.h>
+#include <utils/Errors.h>  // for status_t
+#include <utils/List.h>
+#include <utils/String8.h>
+#include <utils/SystemClock.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+#include <media/AudioPolicyHelper.h>
+#include <media/IMediaHTTPService.h>
+#include <media/IRemoteDisplay.h>
+#include <media/IRemoteDisplayClient.h>
+#include <media/MediaPlayerInterface.h>
+#include <media/mediarecorder.h>
+#include <media/MediaMetadataRetrieverInterface.h>
+#include <media/Metadata.h>
+#include <media/AudioTrack.h>
+#include <media/MemoryLeakTrackUtil.h>
+#include <media/stagefright/MediaCodecList.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooperRoster.h>
+#include <mediautils/BatteryNotifier.h>
+
+//#include <memunreachable/memunreachable.h>
+#include <system/audio.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "MediaAnalyticsService.h"
+
+
+namespace android {
+
+
+static int trackqueue = 0;
+
+//using android::status_t;
+//using android::OK;
+//using android::BAD_VALUE;
+//using android::NOT_ENOUGH_DATA;
+//using android::Parcel;
+
+
+void MediaAnalyticsService::instantiate() {
+    defaultServiceManager()->addService(
+            String16("media.analytics"), new MediaAnalyticsService());
+}
+
+// XXX: add dynamic controls for mMaxRecords
+MediaAnalyticsService::MediaAnalyticsService()
+        : mMaxRecords(100) {
+
+    ALOGD("MediaAnalyticsService created");
+    // clear our queues
+    mOpen = new List<sp<MediaAnalyticsItem>>();
+    mFinalized = new List<sp<MediaAnalyticsItem>>();
+
+    mItemsSubmitted = 0;
+    mItemsFinalized = 0;
+    mItemsDiscarded = 0;
+
+    mLastSessionID = 0;
+    // recover any persistency we set up
+    // etc
+}
+
+MediaAnalyticsService::~MediaAnalyticsService() {
+        ALOGD("MediaAnalyticsService destroyed");
+
+    // XXX: clean out mOpen and mFinalized
+}
+
+
+MediaAnalyticsItem::SessionID_t MediaAnalyticsService::generateUniqueSessionID() {
+    // generate a new sessionid
+
+    Mutex::Autolock _l(mLock_ids);
+    return (++mLastSessionID);
+}
+
+MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(sp<MediaAnalyticsItem> item, bool forcenew) {
+
+    MediaAnalyticsItem::SessionID_t id = MediaAnalyticsItem::SessionIDInvalid;
+
+    // we control these, not using whatever the user might have sent
+    nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+    item->setTimestamp(now);
+    int pid = IPCThreadState::self()->getCallingPid();
+    item->setPid(pid);
+    int uid = IPCThreadState::self()->getCallingUid();
+    item->setUid(uid);
+
+    mItemsSubmitted++;
+
+    // validate the record; we discard if we don't like it
+    if (contentValid(item) == false) {
+        return MediaAnalyticsItem::SessionIDInvalid;
+    }
+
+
+    // if we have a sesisonid in the new record, look to make
+    // sure it doesn't appear in the finalized list.
+    // XXX: this is for security / DOS prevention.
+    // may also require that we persist the unique sessionIDs
+    // across boots [instead of within a single boot]
+
+
+    // match this new record up against records in the open
+    // list...
+    // if there's a match, merge them together
+    // deal with moving the old / merged record into the finalized que
+
+    bool finalizing = item->getFinalized();
+
+    // if finalizing, we'll remove it
+    sp<MediaAnalyticsItem> oitem = findItem(mOpen, item, finalizing | forcenew);
+    if (oitem != NULL) {
+        if (forcenew) {
+            // old one gets finalized, then we insert the new one
+            // so we'll have 2 records at the end of this.
+            // but don't finalize an empty record
+            if (oitem->count() != 0) {
+                oitem->setFinalized(true);
+                saveItem(mFinalized, oitem, 0);
+            }
+            // new record could itself be marked finalized...
+            if (finalizing) {
+                saveItem(mFinalized, item, 0);
+                mItemsFinalized++;
+            } else {
+                saveItem(mOpen, item, 1);
+            }
+            id = item->getSessionID();
+        } else {
+            // combine the records, send it to finalized if appropriate
+            oitem->merge(item);
+            if (finalizing) {
+                saveItem(mFinalized, oitem, 0);
+                mItemsFinalized++;
+            }
+            id = oitem->getSessionID();
+        }
+    } else {
+            // nothing to merge, save the new record
+            if (finalizing) {
+                if (item->count() != 0) {
+                    // drop empty records
+                    saveItem(mFinalized, item, 0);
+                    mItemsFinalized++;
+                }
+            } else {
+                saveItem(mOpen, item, 1);
+            }
+            id = item->getSessionID();
+    }
+
+    return id;
+}
+
+List<sp<MediaAnalyticsItem>> *MediaAnalyticsService::getMediaAnalyticsItemList(bool finished, nsecs_t ts) {
+    // this might never get called; the binder interface maps to the full parm list
+    // on the client side before making the binder call.
+    // but this lets us be sure...
+    List<sp<MediaAnalyticsItem>> *list;
+    list = getMediaAnalyticsItemList(finished, ts, MediaAnalyticsItem::kKeyAny);
+    return list;
+}
+
+List<sp<MediaAnalyticsItem>> *MediaAnalyticsService::getMediaAnalyticsItemList(bool , nsecs_t , MediaAnalyticsItem::Key ) {
+
+    // XXX: implement the get-item-list semantics
+
+    List<sp<MediaAnalyticsItem>> *list = NULL;
+    // set up our query on the persistent data
+    // slurp in all of the pieces
+    // return that
+    return list;
+}
+
+// ignoring 2nd argument, name removed to keep compiler happy
+// XXX: arguments to parse:
+//     -- a timestamp (either since X or last X seconds) to bound search
+status_t MediaAnalyticsService::dump(int fd, const Vector<String16>&)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+        snprintf(buffer, SIZE, "Permission Denial: "
+                "can't dump MediaAnalyticsService from pid=%d, uid=%d\n",
+                IPCThreadState::self()->getCallingPid(),
+                IPCThreadState::self()->getCallingUid());
+        result.append(buffer);
+    } else {
+
+        // crack parameters
+
+
+        Mutex::Autolock _l(mLock);
+
+        snprintf(buffer, SIZE, "Dump of the mediaanalytics process:\n");
+        result.append(buffer);
+
+        int enabled = MediaAnalyticsItem::isEnabled();
+        if (enabled) {
+            snprintf(buffer, SIZE, "Analytics gathering: enabled\n");
+        } else {
+            snprintf(buffer, SIZE, "Analytics gathering: DISABLED via property\n");
+        }
+        result.append(buffer);
+
+        snprintf(buffer, SIZE,
+            "Since Boot: Submissions: %" PRId64
+	    " Finalizations: %" PRId64
+            " Discarded: %" PRId64 "\n",
+            mItemsSubmitted, mItemsFinalized, mItemsDiscarded);
+        result.append(buffer);
+
+        // show the recently recorded records
+        snprintf(buffer, sizeof(buffer), "\nFinalized Analytics (oldest first):\n");
+        result.append(buffer);
+        result.append(this->dumpQueue(mFinalized));
+
+        snprintf(buffer, sizeof(buffer), "\nIn-Progress Analytics (newest first):\n");
+        result.append(buffer);
+        result.append(this->dumpQueue(mOpen));
+
+        // show who is connected and injecting records?
+        // talk about # records fed to the 'readers'
+        // talk about # records we discarded, perhaps "discarded w/o reading" too
+
+    }
+    write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+// caller has locked mLock...
+String8 MediaAnalyticsService::dumpQueue(List<sp<MediaAnalyticsItem>> *theList) {
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+    int slot = 0;
+
+    if (theList->empty()) {
+            result.append("empty\n");
+    } else {
+        List<sp<MediaAnalyticsItem>>::iterator it = theList->begin();
+        for (; it != theList->end(); it++, slot++) {
+            AString entry = (*it)->toString();
+            snprintf(buffer, sizeof(buffer), "%4d: %s\n",
+                        slot, entry.c_str());
+            result.append(buffer);
+        }
+    }
+
+    return result;
+}
+
+//
+// Our Cheap in-core, non-persistent records management.
+// XXX: rewrite this to manage persistence, etc.
+
+// insert appropriately into queue
+void MediaAnalyticsService::saveItem(List<sp<MediaAnalyticsItem>> *l, sp<MediaAnalyticsItem> item, int front) {
+
+    Mutex::Autolock _l(mLock);
+
+    if (false)
+        ALOGD("Inject a record: session %" PRId64 " ts %" PRId64 "",
+            item->getSessionID(), item->getTimestamp());
+
+    if (trackqueue) {
+        String8 before = dumpQueue(l);
+        ALOGD("Q before insert: %s", before.string());
+    }
+
+    // adding at back of queue (fifo order)
+    if (front)  {
+        l->push_front(item);
+    } else {
+        l->push_back(item);
+    }
+
+    if (trackqueue) {
+        String8 after = dumpQueue(l);
+        ALOGD("Q after insert: %s", after.string());
+    }
+
+    // keep removing old records the front until we're in-bounds
+    if (mMaxRecords > 0) {
+        while (l->size() > (size_t) mMaxRecords) {
+            sp<MediaAnalyticsItem> oitem = *(l->begin());
+            if (trackqueue) {
+                ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
+                    oitem->getKey().c_str(), oitem->getSessionID(),
+                    oitem->getTimestamp());
+            }
+            l->erase(l->begin());
+	    mItemsDiscarded++;
+        }
+    }
+
+    if (trackqueue) {
+        String8 after = dumpQueue(l);
+        ALOGD("Q after cleanup: %s", after.string());
+    }
+}
+
+// are they alike enough that nitem can be folded into oitem?
+static bool compatibleItems(sp<MediaAnalyticsItem> oitem, sp<MediaAnalyticsItem> nitem) {
+
+    if (0) {
+        ALOGD("Compare: o %s n %s",
+              oitem->toString().c_str(), nitem->toString().c_str());
+    }
+
+    // general safety
+    if (nitem->getUid() != oitem->getUid()) {
+        return false;
+    }
+    if (nitem->getPid() != oitem->getPid()) {
+        return false;
+    }
+
+    // key -- needs to match
+    if (nitem->getKey() == oitem->getKey()) {
+        // still in the game.
+    } else {
+        return false;
+    }
+
+    // session id -- empty field in new is allowed
+    MediaAnalyticsItem::SessionID_t osession = oitem->getSessionID();
+    MediaAnalyticsItem::SessionID_t nsession = nitem->getSessionID();
+    if (nsession != osession) {
+        // incoming '0' matches value in osession
+        if (nsession != 0) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// find the incomplete record that this will overlay
+sp<MediaAnalyticsItem> MediaAnalyticsService::findItem(List<sp<MediaAnalyticsItem>> *theList, sp<MediaAnalyticsItem> nitem, bool removeit) {
+    sp<MediaAnalyticsItem> item;
+
+    if (nitem == NULL) {
+        return NULL;
+    }
+
+    Mutex::Autolock _l(mLock);
+
+    for (List<sp<MediaAnalyticsItem>>::iterator it = theList->begin();
+        it != theList->end(); it++) {
+        sp<MediaAnalyticsItem> tmp = (*it);
+
+        if (!compatibleItems(tmp, nitem)) {
+            continue;
+        }
+
+        // we match! this is the one I want.
+        if (removeit) {
+            theList->erase(it);
+        }
+        item = tmp;
+        break;
+    }
+    return item;
+}
+
+
+// delete the indicated record
+void MediaAnalyticsService::deleteItem(List<sp<MediaAnalyticsItem>> *l, sp<MediaAnalyticsItem> item) {
+
+    Mutex::Autolock _l(mLock);
+
+    if(trackqueue) {
+        String8 before = dumpQueue(l);
+        ALOGD("Q before delete: %s", before.string());
+    }
+
+    for (List<sp<MediaAnalyticsItem>>::iterator it = l->begin();
+        it != l->end(); it++) {
+        if ((*it)->getSessionID() != item->getSessionID())
+            continue;
+
+        ALOGD(" --- removing record for SessionID %" PRId64 "", item->getSessionID());
+        l->erase(it);
+        break;
+    }
+
+    if (trackqueue) {
+        String8 after = dumpQueue(l);
+        ALOGD("Q after delete: %s", after.string());
+    }
+}
+
+// are the contents good
+bool MediaAnalyticsService::contentValid(sp<MediaAnalyticsItem>) {
+
+    // certain keys require certain uids
+    // internal consistency
+
+    return true;
+}
+
+// are we rate limited, normally false
+bool MediaAnalyticsService::rateLimited(sp<MediaAnalyticsItem>) {
+
+    return false;
+}
+
+
+} // namespace android
diff --git a/media/libmediaanalyticsservice/MediaAnalyticsService.h b/media/libmediaanalyticsservice/MediaAnalyticsService.h
new file mode 100644
index 0000000..f9afeb2
--- /dev/null
+++ b/media/libmediaanalyticsservice/MediaAnalyticsService.h
@@ -0,0 +1,96 @@
+/*
+ * 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_MEDIAANALYTICSSERVICE_H
+#define ANDROID_MEDIAANALYTICSSERVICE_H
+
+#include <arpa/inet.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+
+namespace android {
+
+class MediaAnalyticsService : public BnMediaAnalyticsService
+{
+
+ public:
+
+    virtual int64_t submit(sp<MediaAnalyticsItem> item, bool forcenew);
+
+    virtual List<sp<MediaAnalyticsItem>>
+            *getMediaAnalyticsItemList(bool finished, int64_t ts);
+    virtual List<sp<MediaAnalyticsItem>>
+            *getMediaAnalyticsItemList(bool finished, int64_t ts, MediaAnalyticsItem::Key key);
+
+
+    static  void            instantiate();
+    virtual status_t        dump(int fd, const Vector<String16>& args);
+
+                            MediaAnalyticsService();
+    virtual                 ~MediaAnalyticsService();
+
+ private:
+    MediaAnalyticsItem::SessionID_t generateUniqueSessionID();
+
+    // statistics about our analytics
+    int64_t mItemsSubmitted;
+    int64_t mItemsFinalized;
+    int64_t mItemsDiscarded;
+    MediaAnalyticsItem::SessionID_t mLastSessionID;
+
+    // partitioned a bit so we don't over serialize
+    mutable Mutex           mLock;
+    mutable Mutex           mLock_ids;
+
+    // the most we hold in memory
+    // up to this many in each queue (open, finalized)
+    int32_t mMaxRecords;
+
+    // input validation after arrival from client
+    bool contentValid(sp<MediaAnalyticsItem>);
+    bool rateLimited(sp<MediaAnalyticsItem>);
+
+    // the ones that are still open
+    // (newest at front) since we keep looking for them
+    List<sp<MediaAnalyticsItem>> *mOpen;
+    // the ones we've finalized
+    // (oldest at front) so it prints nicely for dumpsys
+    List<sp<MediaAnalyticsItem>> *mFinalized;
+    // searching within these queues: queue, key
+    sp<MediaAnalyticsItem> findItem(List<sp<MediaAnalyticsItem>> *,
+                                     sp<MediaAnalyticsItem>, bool removeit);
+
+    void saveItem(sp<MediaAnalyticsItem>);
+    void saveItem(List<sp<MediaAnalyticsItem>>*, sp<MediaAnalyticsItem>, int);
+    void deleteItem(List<sp<MediaAnalyticsItem>>*, sp<MediaAnalyticsItem>);
+
+    String8 dumpQueue(List<sp<MediaAnalyticsItem>> *);
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_MEDIAANALYTICSSERVICE_H
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 4956fa0..8761e9d 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -33,7 +33,6 @@
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
-#include "../../libstagefright/include/DRMExtractor.h"
 #include "../../libstagefright/include/NuCachedSource2.h"
 #include "../../libstagefright/include/HTTPBase.h"
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 1476206..f8a6a4e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1658,9 +1658,7 @@
         // directly queuing to display, as this will even improve textureview
         // playback.
         {
-            char value[PROPERTY_VALUE_MAX];
-            if (property_get("persist.sys.media.avsync", value, NULL) &&
-                    (!strcmp("1", value) || !strcasecmp("true", value))) {
+            if (property_get_bool("persist.sys.media.avsync", false)) {
                 format->setInt32("auto-frc", 1);
             }
         }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 3efa54c..6ec79e6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -478,9 +478,7 @@
         notifyListener_l(MEDIA_STOPPED);
     }
 
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("persist.debug.sf.stats", value, NULL) &&
-            (!strcmp("1", value) || !strcasecmp("true", value))) {
+    if (property_get_bool("persist.debug.sf.stats", false)) {
         Vector<String16> args;
         dump(-1, args);
     }
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ce576f9..f247475 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -888,6 +888,8 @@
                 info.mStatus = BufferInfo::OWNED_BY_US;
                 info.mFenceFd = -1;
                 info.mRenderInfo = NULL;
+                info.mGraphicBuffer = NULL;
+                info.mNewGraphicBuffer = false;
 
                 if (mode == IOMX::kPortModePresetSecureBuffer) {
                     void *ptr = NULL;
@@ -1127,6 +1129,7 @@
         info.mIsReadFence = false;
         info.mRenderInfo = NULL;
         info.mGraphicBuffer = graphicBuffer;
+        info.mNewGraphicBuffer = false;
 
         // TODO: We shouln't need to create MediaCodecBuffer. In metadata mode
         //       OMX doesn't use the shared memory buffer, but some code still
@@ -1201,6 +1204,7 @@
         info.mFenceFd = -1;
         info.mRenderInfo = NULL;
         info.mGraphicBuffer = NULL;
+        info.mNewGraphicBuffer = false;
         info.mDequeuedAt = mDequeueCounter;
 
         info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));
@@ -1232,7 +1236,7 @@
     }
 
     ALOGV("[%s] submitting output meta buffer ID %u for graphic buffer %p",
-          mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer.get());
+          mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer->handle);
 
     --mMetadataBuffersToSubmit;
     info->checkWriteFence("submitOutputMetadataBuffer");
@@ -1373,7 +1377,11 @@
                     break;
                 }
 
-                ALOGV("dequeued buffer %p", info->mGraphicBuffer->getNativeBuffer());
+                ALOGV("dequeued buffer #%u with age %u, graphicBuffer %p",
+                        (unsigned)(info - &mBuffers[kPortIndexOutput][0]),
+                        mDequeueCounter - info->mDequeuedAt,
+                        info->mGraphicBuffer->handle);
+
                 info->mStatus = BufferInfo::OWNED_BY_US;
                 info->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow");
                 updateRenderInfoForDequeuedBuffer(buf, fenceFd, info);
@@ -1419,6 +1427,7 @@
 
     // discard buffer in LRU info and replace with new buffer
     oldest->mGraphicBuffer = new GraphicBuffer(buf, false);
+    oldest->mNewGraphicBuffer = true;
     oldest->mStatus = BufferInfo::OWNED_BY_US;
     oldest->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow for oldest");
     mRenderTracker.untrackFrame(oldest->mRenderInfo);
@@ -1427,7 +1436,7 @@
     ALOGV("replaced oldest buffer #%u with age %u, graphicBuffer %p",
             (unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
             mDequeueCounter - oldest->mDequeuedAt,
-            oldest->mGraphicBuffer->getNativeBuffer());
+            oldest->mGraphicBuffer->handle);
 
     updateRenderInfoForDequeuedBuffer(buf, fenceFd, oldest);
     return oldest;
@@ -1539,7 +1548,13 @@
 
 status_t ACodec::fillBuffer(BufferInfo *info) {
     status_t err;
-    if (!storingMetadataInDecodedBuffers()) {
+    // Even in dynamic ANW buffer mode, if the graphic buffer is not changing,
+    // send sPreset instead of the same graphic buffer, so that OMX server
+    // side doesn't update the meta. In theory it should make no difference,
+    // however when the same buffer is parcelled again, a new handle could be
+    // created on server side, and some decoder doesn't recognize the handle
+    // even if it's the same buffer.
+    if (!storingMetadataInDecodedBuffers() || !info->mNewGraphicBuffer) {
         err = mOMXNode->fillBuffer(
             info->mBufferID, OMXBuffer::sPreset, info->mFenceFd);
     } else {
@@ -1547,6 +1562,7 @@
             info->mBufferID, info->mGraphicBuffer, info->mFenceFd);
     }
 
+    info->mNewGraphicBuffer = false;
     info->mFenceFd = -1;
     if (err == OK) {
         info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 990d4b7..0fe44eb 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -770,9 +770,7 @@
         return mInitCheck;
     }
 
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("media.stagefright.record-stats", value, NULL)
-        && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+    if (property_get_bool("media.stagefright.record-stats", false)) {
         mCollectStats = true;
     }
 
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index a9536b9..4a965ba 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -16,23 +16,9 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "DataSource"
 
-#include "include/AMRExtractor.h"
-
-#include "include/AACExtractor.h"
 #include "include/CallbackDataSource.h"
-#include "include/DRMExtractor.h"
-#include "include/FLACExtractor.h"
 #include "include/HTTPBase.h"
-#include "include/MidiExtractor.h"
-#include "include/MP3Extractor.h"
-#include "include/MPEG2PSExtractor.h"
-#include "include/MPEG2TSExtractor.h"
-#include "include/MPEG4Extractor.h"
 #include "include/NuCachedSource2.h"
-#include "include/OggExtractor.h"
-#include "include/WAVExtractor.h"
-
-#include "matroska/MatroskaExtractor.h"
 
 #include <media/IMediaHTTPConnection.h>
 #include <media/IMediaHTTPService.h>
@@ -43,12 +29,15 @@
 #include <media/stagefright/FileSource.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaHTTP.h>
+#include <media/stagefright/Utils.h>
 #include <utils/String8.h>
 
 #include <cutils/properties.h>
 
 #include <private/android_filesystem_config.h>
 
+#include <arpa/inet.h>
+
 namespace android {
 
 bool DataSource::getUInt16(off64_t offset, uint16_t *x) {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index ee603a4..f9af5e1 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1022,7 +1022,9 @@
                         while (cur && cur->next != mLastTrack) {
                             cur = cur->next;
                         }
-                        cur->next = NULL;
+                        if (cur) {
+                            cur->next = NULL;
+                        }
                         delete mLastTrack;
                         mLastTrack = cur;
                     }
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 9978b76..e57057c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1128,9 +1128,7 @@
 
     // Test mode is enabled only if rw.media.record.test system
     // property is enabled.
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("rw.media.record.test", value, NULL) &&
-        (!strcasecmp(value, "true") || !strcasecmp(value, "1"))) {
+    if (property_get_bool("rw.media.record.test", false)) {
         return true;
     }
     return false;
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 9362a07..aeaead5 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -143,9 +143,7 @@
         const sp<DataSource> &source, const char *mime) {
     ALOGV("MediaExtractor::Create %s", mime);
 
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("media.stagefright.extractremote", value, NULL)
-            && (!strcmp("0", value) || !strcasecmp("false", value))) {
+    if (!property_get_bool("media.stagefright.extractremote", true)) {
         // local extractor
         ALOGW("creating media extractor in calling process");
         return CreateFromService(source, mime);
@@ -329,9 +327,7 @@
     RegisterSniffer_l(SniffMPEG2PS);
     RegisterSniffer_l(SniffMidi);
 
-    char value[PROPERTY_VALUE_MAX];
-    if (property_get("drm.service.enabled", value, NULL)
-            && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+    if (property_get_bool("drm.service.enabled", false)) {
         RegisterSniffer_l(SniffDRM);
     }
     gSniffersRegistered = true;
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index c593eb5..f2638ed 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -1659,9 +1659,7 @@
         return AString("<URI suppressed>");
     }
 
-    char prop[PROPERTY_VALUE_MAX];
-    if (property_get("media.stagefright.log-uri", prop, "false") &&
-        (!strcmp(prop, "1") || !strcmp(prop, "true"))) {
+    if (property_get_bool("media.stagefright.log-uri", false)) {
         return uri;
     }
 
diff --git a/media/libstagefright/foundation/tests/AData_test.cpp b/media/libstagefright/foundation/tests/AData_test.cpp
new file mode 100644
index 0000000..f014c25
--- /dev/null
+++ b/media/libstagefright/foundation/tests/AData_test.cpp
@@ -0,0 +1,981 @@
+/*
+ * 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 "AData_test"
+
+#include <gtest/gtest.h>
+#include <utils/RefBase.h>
+//#include <utils/StrongPointer.h>
+
+#include <media/stagefright/foundation/AData.h>
+#include <media/stagefright/foundation/ABuffer.h>
+
+namespace android {
+
+class ADataTest : public ::testing::Test {
+};
+
+// ============ AUnion
+
+struct Events {
+    int dtor;
+    int ctor_empty;
+    int ctor_copy;
+};
+
+struct EventCounter : public RefBase {
+    EventCounter(int *counter, int magic=1234) : mCounter(counter), mMagic(magic) { }
+    virtual ~EventCounter() { ++*mCounter; mMagic = 0; }
+    int magic() const { return mMagic; }
+private:
+    int *mCounter;
+    int mMagic;
+};
+
+struct DerivedCounter : public EventCounter {
+    DerivedCounter(int *counter, int magic=1234) : EventCounter(counter, magic) { }
+};
+
+TEST_F(ADataTest, AUnion_Test) {
+    AUnion<int, const char *, char> u;
+    u.emplace<int>(4);
+    u.del<int>();
+    EXPECT_EQ(4, u.get<int>()); // verify that del<> is a no-op for trivial types, such as int.
+                                // specifically, verify that it does not clear the objet memory
+
+    u.emplace<const char *>("hello");
+    EXPECT_STREQ("hello", u.get<const char *>());
+    u.del<const char *>();
+
+    // u.del<char *>();
+    // u.emplace<const int>(4);
+    u.emplace<void>();
+    u.del<void>();
+
+    u.emplace<int>(~0);
+    u.del<int>();
+    EXPECT_EQ(~0, u.get<int>());
+    u.emplace<char>(0x15);
+    // verify that rest of memory after char is cleared upon construction
+    EXPECT_EQ(0, memcmp((char *)(&u) + sizeof(char), "\0\0\0", 3));
+    EXPECT_EQ(0x15, u.get<char>());
+    u.del<char>();
+
+    AUnion<EventCounter, EventCounter *> d;
+    int destructions = 0;
+
+    d.emplace<EventCounter>(&destructions);
+    d.del<EventCounter>();
+    EXPECT_EQ(1, destructions);
+
+    EventCounter *ctr = new EventCounter(&destructions);
+    d.emplace<EventCounter *>(ctr);
+    d.del<EventCounter *>();
+    EXPECT_EQ(1, destructions);
+
+    delete ctr;
+    EXPECT_EQ(2, destructions);
+
+    AUnion<std::shared_ptr<EventCounter>, std::unique_ptr<EventCounter>> md;
+    md.emplace<std::shared_ptr<EventCounter>>(new EventCounter(&destructions));
+    std::shared_ptr<EventCounter> copy(md.get<std::shared_ptr<EventCounter>>());
+    std::weak_ptr<EventCounter> weak(copy);
+    EXPECT_EQ(2, destructions);
+
+    copy.reset();
+    EXPECT_EQ(2, destructions);
+    md.del<std::shared_ptr<EventCounter>>();
+    EXPECT_EQ(3, destructions);
+    EXPECT_TRUE(weak.expired());
+
+    md.emplace<std::unique_ptr<EventCounter>>(new EventCounter(&destructions));
+    EXPECT_EQ(3, destructions);
+
+    std::unique_ptr<EventCounter> unique = std::move(md.get<std::unique_ptr<EventCounter>>());
+    EXPECT_EQ(3, destructions);
+    EXPECT_FALSE((bool)md.get<std::unique_ptr<EventCounter>>());
+
+    md.del<std::unique_ptr<EventCounter>>();
+    EXPECT_EQ(3, destructions);
+    md.emplace<std::unique_ptr<EventCounter>>(std::move(unique));
+    EXPECT_TRUE((bool)md.get<std::unique_ptr<EventCounter>>());
+    EXPECT_EQ(3, destructions);
+
+    md.del<std::unique_ptr<EventCounter>>();
+    EXPECT_EQ(4, destructions);
+}
+
+TEST_F(ADataTest, AData_StaticTest) {
+    using namespace std;
+
+    static_assert(is_copy_assignable<shared_ptr<EventCounter>>::value, "");
+    static_assert(is_copy_constructible<shared_ptr<EventCounter>>::value, "");
+    static_assert(is_default_constructible<shared_ptr<EventCounter>>::value, "");
+
+    static_assert(is_copy_assignable<weak_ptr<DerivedCounter>>::value, "");
+    static_assert(is_copy_constructible<weak_ptr<DerivedCounter>>::value, "");
+    static_assert(is_default_constructible<weak_ptr<DerivedCounter>>::value, "");
+
+    static_assert(!is_copy_assignable<unique_ptr<DerivedCounter>>::value, "");
+    static_assert(!is_copy_constructible<unique_ptr<DerivedCounter>>::value, "");
+    static_assert(is_default_constructible<unique_ptr<DerivedCounter>>::value, "");
+
+    static_assert(is_copy_assignable<sp<EventCounter>>::value, "");
+    static_assert(is_copy_constructible<sp<EventCounter>>::value, "");
+    static_assert(is_default_constructible<sp<EventCounter>>::value, "");
+
+    static_assert(is_copy_assignable<wp<EventCounter>>::value, "");
+    static_assert(is_copy_constructible<wp<EventCounter>>::value, "");
+    static_assert(is_default_constructible<wp<EventCounter>>::value, "");
+
+    static_assert(is_convertible<shared_ptr<DerivedCounter>, shared_ptr<EventCounter>>::value, "");
+    static_assert(!is_convertible<shared_ptr<EventCounter>, shared_ptr<DerivedCounter>>::value, "");
+
+    static_assert(is_convertible<unique_ptr<DerivedCounter>, unique_ptr<EventCounter>>::value, "");
+    static_assert(!is_convertible<unique_ptr<EventCounter>, unique_ptr<DerivedCounter>>::value, "");
+
+    static_assert(is_convertible<unique_ptr<DerivedCounter>, shared_ptr<EventCounter>>::value, "");
+    static_assert(!is_convertible<shared_ptr<DerivedCounter>, unique_ptr<EventCounter>>::value, "");
+
+    static_assert(is_convertible<weak_ptr<DerivedCounter>, weak_ptr<EventCounter>>::value, "");
+    static_assert(!is_convertible<weak_ptr<EventCounter>, weak_ptr<DerivedCounter>>::value, "");
+
+    static_assert(is_convertible<shared_ptr<DerivedCounter>, weak_ptr<EventCounter>>::value, "");
+    static_assert(!is_convertible<weak_ptr<DerivedCounter>, shared_ptr<EventCounter>>::value, "");
+
+    static_assert(is_convertible<sp<EventCounter>, sp<RefBase>>::value, "");
+    static_assert(is_convertible<sp<RefBase>, sp<EventCounter>>::value, "YES");
+
+    static_assert(is_convertible<wp<EventCounter>, wp<RefBase>>::value, "");
+    static_assert(is_convertible<wp<RefBase>, wp<EventCounter>>::value, "YES");
+
+    static_assert(is_convertible<sp<EventCounter>, wp<RefBase>>::value, "");
+    static_assert(!is_convertible<wp<EventCounter>, sp<RefBase>>::value, "");
+}
+
+TEST_F(ADataTest, AData_SampleTest) {
+    AData<int, float>::Basic data;
+    int i = 1;
+    float f = 7.0f;
+
+    data.set(5);
+    EXPECT_TRUE(data.find(&i));
+    EXPECT_FALSE(data.find(&f));
+    EXPECT_EQ(i, 5);
+
+    data.set(6.0f);
+    EXPECT_FALSE(data.find(&i));
+    EXPECT_TRUE(data.find(&f));
+    EXPECT_EQ(f, 6.0f);
+
+    AData<int, sp<RefBase>>::RelaxedBasic objdata; // relaxed type support
+    sp<ABuffer> buf = new ABuffer(16), buf2;
+    sp<RefBase> obj;
+
+    objdata.set(buf);
+    EXPECT_TRUE(objdata.find(&buf2));
+    EXPECT_EQ(buf, buf2);
+    EXPECT_FALSE(objdata.find(&i));
+    EXPECT_TRUE(objdata.find(&obj));
+    EXPECT_TRUE(obj == buf);
+
+    obj = buf;
+    objdata.set(obj); // storing as sp<RefBase>
+    EXPECT_FALSE(objdata.find(&buf2));  // not stored as ABuffer(!)
+    EXPECT_TRUE(objdata.find(&obj));
+}
+
+struct SampleTypeFlagger {
+    typedef unsigned type;
+    enum Flags : type {
+        kEmpty = 100,
+        kInt,
+        kConstCharPtr,
+        kEventCounter,
+        kEventCounterPointer,
+        kEventCounterSharedPointer,
+        kEventCounterUniquePointer,
+        kEventCounterWeakPointer,
+        kEventCounterSP,
+        kEventCounterWP,
+    };
+    constexpr static type mask = ~Flags(0);
+    constexpr static type flagFor(void*) { return kEmpty; }
+    constexpr static type flagFor(int*) { return kInt; }
+    constexpr static type flagFor(const char**) { return kConstCharPtr; }
+    constexpr static type flagFor(EventCounter*) { return kEventCounter; }
+    constexpr static type flagFor(EventCounter**) { return kEventCounterPointer; }
+    constexpr static
+    type flagFor(std::shared_ptr<EventCounter>*) { return kEventCounterSharedPointer; }
+    constexpr static
+    type flagFor(std::unique_ptr<EventCounter>*) { return kEventCounterUniquePointer; }
+    constexpr static type flagFor(std::weak_ptr<EventCounter>*) { return kEventCounterWeakPointer; }
+    constexpr static type flagFor(sp<EventCounter>*) { return kEventCounterSP; }
+    constexpr static type flagFor(wp<EventCounter>*) { return kEventCounterWP; }
+    constexpr static bool canDeleteAs(type object, type del) { return del == object; }
+    template <typename T> struct store { typedef T as_type; };
+};
+
+TEST_F(ADataTest, AData_SimpleTest) {
+    int _int = 0;
+    const char *_constCharPtr = NULL;
+    AData<int, const char *>::Custom<SampleTypeFlagger> u;
+    EXPECT_FALSE(u.used());
+    EXPECT_FALSE(u.find<int>(&_int));
+    EXPECT_FALSE(u.find<const char *>(&_constCharPtr));
+
+    EXPECT_TRUE(u.set<int>(4));
+    EXPECT_TRUE(u.used());
+    EXPECT_TRUE(u.find<int>(&_int));
+    EXPECT_EQ(4, _int);
+    EXPECT_FALSE(u.find<const char *>(&_constCharPtr));
+    EXPECT_EQ(NULL, _constCharPtr);
+
+    EXPECT_TRUE(u.clear());
+    EXPECT_FALSE(u.used());
+    EXPECT_FALSE(u.find<int>(&_int));
+    EXPECT_FALSE(u.find<const char *>(&_constCharPtr));
+
+    EXPECT_TRUE(u.set<int>(5));
+    EXPECT_TRUE(u.set<int>(6));
+    EXPECT_TRUE(u.find<int>(&_int));
+    EXPECT_EQ(6, _int);
+
+    EXPECT_TRUE(u.set<const char *>("hello"));
+    EXPECT_TRUE(u.used());
+    EXPECT_FALSE(u.find<int>(&_int));
+    EXPECT_TRUE(u.find<const char *>(&_constCharPtr));
+    EXPECT_STREQ("hello", _constCharPtr);
+
+    EXPECT_TRUE(u.clear());
+    EXPECT_FALSE(u.used());
+    EXPECT_FALSE(u.find<int>(&_int));
+    EXPECT_FALSE(u.find<const char *>(&_constCharPtr));
+
+    EXPECT_TRUE(u.set<const char *>("world"));
+    EXPECT_TRUE(u.set<const char *>("!!"));
+    EXPECT_TRUE(u.used());
+    EXPECT_FALSE(u.find<int>(&_int));
+    EXPECT_TRUE(u.find<const char *>(&_constCharPtr));
+    EXPECT_STREQ("!!", _constCharPtr);
+
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_TRUE(u.find(&_constCharPtr));
+}
+
+void set(std::unique_ptr<int> &dst, std::unique_ptr<int> &&src) {
+    dst = std::move(src);
+}
+
+void set(std::unique_ptr<int> &dst, std::unique_ptr<int> &src) {
+    dst = std::move(src);
+}
+
+TEST_F(ADataTest, AData_CopyMoveTest) {
+    int destructions = 0;
+    int _int = 0;
+    std::shared_ptr<EventCounter> _shared;
+    std::unique_ptr<EventCounter> _unique;
+    std::weak_ptr<EventCounter> _weak;
+    const std::shared_ptr<EventCounter> _constShared(new EventCounter(&destructions));
+    const std::unique_ptr<EventCounter> _constUnique = nullptr;
+
+    AData<int, std::weak_ptr<EventCounter>, std::shared_ptr<EventCounter>,
+            std::unique_ptr<EventCounter>>::Basic u;
+
+    // test that data is empty
+    EXPECT_FALSE(u.used());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_weak));
+
+    // test that integer can be stored and read
+    EXPECT_TRUE(u.set<int>(1));
+    EXPECT_TRUE(u.used());
+    EXPECT_TRUE(u.find(&_int));
+    EXPECT_EQ(1, _int);
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_weak));
+
+    // test that movable type (unique_ptr) can be moved in and read out, and it moves
+    _unique = std::unique_ptr<EventCounter>(new EventCounter(&destructions, 123));
+    EXPECT_TRUE(u.set(std::move(_unique)));
+    EXPECT_FALSE((bool)_unique);
+    EXPECT_TRUE(u.used());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_TRUE(u.remove(&_unique));
+    EXPECT_TRUE((bool)_unique);
+    if (_unique) {
+        EXPECT_EQ(123, _unique->magic());
+    }
+
+    // the unique value should have been removed but still accessible as nullptr
+    EXPECT_TRUE(u.remove(&_unique));
+    EXPECT_FALSE((bool)_unique);
+    EXPECT_EQ(1, destructions);
+
+    // test that movable-only type (unique_ptr) can be stored without moving (and is still
+    // moved)
+    _unique = std::unique_ptr<EventCounter>(new EventCounter(&destructions, 321));
+    EXPECT_TRUE(u.set(std::move(_unique)));
+    EXPECT_FALSE((bool)_unique);
+    EXPECT_TRUE(u.set(std::unique_ptr<EventCounter>(new EventCounter(&destructions, 1234))));
+    EXPECT_EQ(2, destructions);
+    EXPECT_TRUE(u.remove(&_unique));
+    EXPECT_TRUE((bool)_unique);
+    if (_unique) {
+        EXPECT_EQ(1234, _unique->magic());
+    }
+    EXPECT_TRUE(u.set(std::move(_unique)));
+    EXPECT_EQ(2, destructions);
+    EXPECT_TRUE(u.clear());
+    EXPECT_EQ(3, destructions);
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_weak));
+
+    // u.set(_constUnique);
+
+    // test that copiable & movable type (shared_ptr) is copied unless explicitly moved.
+    _shared = std::make_shared<EventCounter>(&destructions, 234);
+    EXPECT_EQ(1L, _shared.use_count());
+    EXPECT_TRUE(u.set(_shared));
+    EXPECT_TRUE((bool)_shared);
+    if (_shared) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+
+    EXPECT_EQ(2L, _shared.use_count());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_TRUE(u.find(&_shared));
+    EXPECT_EQ(2L, _shared.use_count());
+    EXPECT_TRUE((bool)_shared);
+    if (_shared) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+
+    // explicitly move in shared_ptr
+    EXPECT_TRUE(u.set(std::move(_shared)));
+    EXPECT_EQ(0, _shared.use_count()); // shared should be nullptr
+    EXPECT_FALSE((bool)_shared);
+    EXPECT_TRUE(u.find(&_shared));
+    EXPECT_EQ(2L, _shared.use_count()); // now both u and _shared contains the object
+    EXPECT_TRUE((bool)_shared);
+    if (_shared) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_TRUE(u.find(&_shared));
+    EXPECT_EQ(2L, _shared.use_count()); // still both u and _shared contains the object
+
+    EXPECT_TRUE(u.clear());
+    EXPECT_TRUE(_shared.unique()); // now only _shared contains the object
+
+    EXPECT_TRUE(u.set(_constShared));
+    EXPECT_EQ(2L, _constShared.use_count()); // even though it is const, we can add a use count
+    EXPECT_TRUE(u.find(&_shared));
+    EXPECT_EQ(3L, _shared.use_count()); // now u, _shared and _constShared contains the const object
+    EXPECT_TRUE((bool)_shared);
+    if (_shared) {
+        EXPECT_EQ(1234, _shared->magic());
+    }
+
+    // test that weak pointer can be copied in (support for moving is from C++14 only)
+    _weak = _shared;
+    EXPECT_EQ(_weak.use_count(), _shared.use_count());
+    EXPECT_TRUE(u.set(_weak));
+
+    _weak.reset();
+    EXPECT_EQ(_weak.use_count(), 0);
+
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_TRUE(u.find(&_weak));
+    EXPECT_EQ(_weak.use_count(), _shared.use_count());
+    EXPECT_EQ(_weak.lock(), _shared);
+
+    // we can remove a weak pointer multiple times
+    _weak.reset();
+    EXPECT_TRUE(u.find(&_weak));
+    EXPECT_EQ(_weak.use_count(), _shared.use_count());
+    EXPECT_EQ(_weak.lock(), _shared);
+    EXPECT_TRUE(u.clear());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+};
+
+TEST_F(ADataTest, AData_RelaxedCopyMoveTest) {
+    int destructions = 0;
+    int _int = 0;
+    std::shared_ptr<DerivedCounter> _shared;
+    std::unique_ptr<DerivedCounter> _unique, _unique2;
+    std::weak_ptr<DerivedCounter> _weak;
+    std::shared_ptr<EventCounter> _shared_base;
+    std::unique_ptr<EventCounter> _unique_base;
+    std::weak_ptr<EventCounter> _weak_base;
+    const std::shared_ptr<DerivedCounter> _constShared(new DerivedCounter(&destructions));
+    const std::unique_ptr<DerivedCounter> _constUnique = nullptr;
+
+    AData<int, std::unique_ptr<EventCounter>, std::shared_ptr<EventCounter>,
+            std::weak_ptr<EventCounter>>::RelaxedBasic u;
+
+    // test that data is empty
+    EXPECT_FALSE(u.used());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_FALSE(u.remove(&_unique_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // test that integer can be stored and read
+    EXPECT_TRUE(u.set<int>(1));
+    EXPECT_TRUE(u.used());
+    EXPECT_TRUE(u.find(&_int));
+    EXPECT_EQ(1, _int);
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_FALSE(u.remove(&_unique_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // test that movable type (unique_ptr) can be moved in and read out, and it moves
+    _unique = std::unique_ptr<DerivedCounter>(new DerivedCounter(&destructions, 123));
+    EXPECT_TRUE(u.set(std::move(_unique)));
+    EXPECT_FALSE((bool)_unique);
+    EXPECT_TRUE(u.used());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_TRUE(u.remove(&_unique));
+    EXPECT_TRUE((bool)_unique);
+    if (_unique) {
+        EXPECT_EQ(123, _unique->magic());
+    }
+
+    // the unique value should have been removed but still accessible as nullptr
+    EXPECT_TRUE(u.remove(&_unique));
+    EXPECT_FALSE((bool)_unique);
+    EXPECT_EQ(1, destructions);
+
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_TRUE(u.remove(&_unique_base));
+    EXPECT_FALSE((bool)_unique_base);
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // test that movable-only type (unique_ptr) can be stored without moving (and is still
+    // moved)
+    _unique = std::unique_ptr<DerivedCounter>(new DerivedCounter(&destructions, 321));
+    EXPECT_TRUE(u.set(std::move(_unique)));
+    EXPECT_FALSE((bool)_unique);
+    EXPECT_TRUE(u.set(std::unique_ptr<DerivedCounter>(new DerivedCounter(&destructions, 1234))));
+    EXPECT_EQ(2, destructions);
+    EXPECT_TRUE(u.remove(&_unique));
+    EXPECT_TRUE((bool)_unique);
+    if (_unique) {
+        EXPECT_EQ(1234, _unique->magic());
+    }
+    EXPECT_TRUE(u.set(std::move(_unique)));
+    EXPECT_EQ(2, destructions);
+    EXPECT_TRUE(u.clear());
+    EXPECT_EQ(3, destructions);
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_FALSE(u.remove(&_unique_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // test that unique pointer can be set and removed as base type (but removed as derived only
+    // if it was set as derived type)
+    _unique = std::unique_ptr<DerivedCounter>(new DerivedCounter(&destructions, 321));
+    EXPECT_TRUE(u.set(std::move(_unique)));
+    EXPECT_FALSE((bool)_unique);
+    EXPECT_TRUE(u.remove(&_unique_base));
+    EXPECT_TRUE((bool)_unique_base);
+    if (_unique_base) {
+        EXPECT_EQ(321, _unique_base->magic());
+    }
+    EXPECT_TRUE(u.remove(&_unique));
+    EXPECT_FALSE((bool)_unique);
+
+    EXPECT_TRUE(u.set(std::move(_unique_base)));
+    EXPECT_FALSE((bool)_unique_base);
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE((bool)_unique);
+    EXPECT_TRUE(u.remove(&_unique_base));
+    EXPECT_TRUE((bool)_unique_base);
+    if (_unique_base) {
+        EXPECT_EQ(321, _unique_base->magic());
+    }
+
+    EXPECT_EQ(3, destructions);
+    EXPECT_TRUE(u.remove(&_unique_base));
+    EXPECT_EQ(4, destructions);
+    EXPECT_FALSE((bool)_unique_base);
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // u.set(_constUnique);
+
+    // test that copiable & movable type (shared_ptr) is copied unless explicitly moved.
+    _shared = std::make_shared<DerivedCounter>(&destructions, 234);
+    EXPECT_EQ(1L, _shared.use_count());
+    EXPECT_TRUE(u.set(_shared));
+    EXPECT_TRUE((bool)_shared);
+    if (_shared) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+
+    EXPECT_EQ(2L, _shared.use_count());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_TRUE(u.find(&_shared));
+    EXPECT_FALSE(u.remove(&_unique_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+    EXPECT_EQ(2L, _shared.use_count());
+    EXPECT_TRUE((bool)_shared);
+    if (_shared) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+
+    // explicitly move in shared_ptr
+    EXPECT_TRUE(u.set(std::move(_shared)));
+    EXPECT_EQ(0, _shared.use_count()); // shared should be nullptr
+    EXPECT_FALSE((bool)_shared);
+    EXPECT_TRUE(u.find(&_shared));
+    EXPECT_EQ(2L, _shared.use_count()); // now both u and _shared contains the object
+    EXPECT_TRUE((bool)_shared);
+    if (_shared) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.remove(&_unique_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+    EXPECT_TRUE(u.find(&_shared));
+    EXPECT_EQ(2L, _shared.use_count()); // still both u and _shared contains the object
+
+    EXPECT_TRUE(u.clear());
+    EXPECT_TRUE(_shared.unique()); // now only _shared contains the object
+
+    EXPECT_TRUE(u.set(_constShared));
+    EXPECT_EQ(2L, _constShared.use_count()); // even though it is const, we can add a use count
+    EXPECT_TRUE(u.find(&_shared));
+    EXPECT_EQ(3L, _shared.use_count()); // now u, _shared and _constShared contains the const object
+    EXPECT_TRUE((bool)_shared);
+    if (_shared) {
+        EXPECT_EQ(1234, _shared->magic());
+    }
+
+    // test that shared pointer can be set and removed as base type (but removed as derived only
+    // if it was set as derived type)
+    EXPECT_TRUE(u.find(&_shared_base));
+    EXPECT_TRUE((bool)_shared_base);
+    if (_shared_base) {
+        EXPECT_EQ(1234, _shared_base->magic());
+    }
+    EXPECT_EQ(4L, _shared.use_count()); // now u, _shared, _constShared and _shared_base contains
+                                        // the const object
+    _shared.reset();
+    EXPECT_EQ(3L, _shared_base.use_count()); // now u, _constShared and _shared_base contains it
+    EXPECT_TRUE(u.clear());
+    EXPECT_EQ(2L, _shared_base.use_count()); // now _constShared and _shared_base contains it
+
+    EXPECT_TRUE(u.set(_shared_base));        // now u_ also contains it as base class
+    EXPECT_EQ(3L, _shared_base.use_count());
+    EXPECT_FALSE(u.find(&_shared)); // cannot get it as derived type
+    EXPECT_FALSE((bool)_shared);
+    _shared_base.reset();
+    EXPECT_TRUE(u.find(&_shared_base)); // can still get it as base type
+    EXPECT_TRUE((bool)_shared_base);
+    if (_shared_base) {
+        EXPECT_EQ(1234, _shared_base->magic());
+    }
+    _shared = std::static_pointer_cast<DerivedCounter>(_shared_base);
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.remove(&_unique_base));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // test that weak pointer can be copied in (support for moving is from C++14 only)
+    _weak = _shared;
+    EXPECT_EQ(_weak.use_count(), _shared.use_count());
+    EXPECT_TRUE(u.set(_weak));
+
+    _weak.reset();
+    EXPECT_EQ(_weak.use_count(), 0);
+
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.remove(&_unique_base));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_TRUE(u.find(&_weak));
+    EXPECT_EQ(_weak.use_count(), _shared.use_count());
+    EXPECT_EQ(_weak.lock(), _shared);
+
+    // we can remove a weak pointer multiple times
+    _weak.reset();
+    EXPECT_TRUE(u.find(&_weak));
+    EXPECT_EQ(_weak.use_count(), _shared.use_count());
+    EXPECT_EQ(_weak.lock(), _shared);
+    EXPECT_TRUE(u.clear());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.remove(&_unique_base));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // test that weak pointer can be set and removed as base type (but removed as derived only
+    // if it was set as derived type)
+    _weak = _shared;
+    EXPECT_TRUE(u.set(_weak));
+    EXPECT_TRUE(u.find(&_weak_base));
+    EXPECT_FALSE(_weak_base.expired());
+    if (!_weak_base.expired()) {
+        EXPECT_EQ(1234, _weak_base.lock()->magic());
+    }
+    // now _shared, _constShared and _shared_base contains the const object
+    EXPECT_EQ(3L, _weak.use_count());
+    _weak.reset();
+    EXPECT_EQ(3L, _weak_base.use_count()); // _weak did not hold a reference
+    _shared.reset();
+    EXPECT_EQ(2L, _weak_base.use_count()); // now u, _constShared and _shared_base contains it
+    EXPECT_TRUE(u.clear());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.remove(&_unique));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.remove(&_unique_base));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    EXPECT_TRUE(u.set(_weak_base)); // now u_ also contains it as base class
+    EXPECT_FALSE(u.find(&_weak));   // cannot get it as derived type
+    EXPECT_TRUE(_weak.expired());
+    _weak_base.reset();
+    EXPECT_TRUE(u.find(&_weak_base)); // can still get it as base type
+    EXPECT_FALSE(_weak_base.expired());
+    if (!_weak_base.expired()) {
+        EXPECT_EQ(1234, _weak_base.lock()->magic());
+    }
+};
+
+TEST_F(ADataTest, AData_AndroidSpTest) {
+    int destructions = 0;
+    int _int = 0;
+    sp<EventCounter> _shared;
+    wp<EventCounter> _weak;
+    const sp<EventCounter> _constShared(new EventCounter(&destructions));
+
+    AData<int, sp<EventCounter>, wp<EventCounter>>::Strict<uint8_t> u;
+
+    // test that data is empty
+    EXPECT_FALSE(u.used());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+
+    // test that integer can be stored and read
+    EXPECT_TRUE(u.set<int>(1));
+    EXPECT_TRUE(u.used());
+    EXPECT_TRUE(u.find(&_int));
+    EXPECT_EQ(1, _int);
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+
+    // test that copiable & movable type (shared_ptr) is copied unless explicitly moved.
+    _shared = new EventCounter(&destructions, 234);
+    _weak = _shared; // used for tracking #234
+
+    EXPECT_TRUE(u.set(_shared));
+    EXPECT_TRUE((bool)_shared.get());
+    if (_shared.get()) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+
+    _shared.clear();
+    EXPECT_EQ(NULL, _shared.get());
+    EXPECT_NE(nullptr, _weak.promote().get()); // u still holds object
+
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_TRUE(u.find(&_shared)); // now u and _shared both hold object
+    EXPECT_TRUE((bool)_shared.get());
+    if (_shared.get()) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+    // verify the find did not move out object
+    _shared.clear();
+    EXPECT_EQ(NULL, _shared.get());
+    EXPECT_NE(nullptr, _weak.promote().get()); // u still holds object
+    EXPECT_TRUE(u.find(&_shared)); // now u and _shared both hold object
+    if (_shared.get()) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+
+    // verify that we can set object multiple times
+    EXPECT_TRUE(u.set(_shared));
+
+    // explicitly move in sp
+    EXPECT_TRUE(u.set(std::move(_shared)));
+    EXPECT_FALSE((bool)_shared.get()); // android also clears sp<> on move...
+    EXPECT_TRUE(u.find(&_shared)); // still can get it back
+    EXPECT_TRUE((bool)_shared.get());
+    if (_shared.get()) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_weak));
+
+    EXPECT_TRUE(u.used());
+    EXPECT_TRUE(u.clear()); // now only _shared contains the object
+    EXPECT_FALSE(u.used());
+
+    // we still hold a copy
+    EXPECT_TRUE((bool)_shared.get());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared)); // _shared still contains the object
+
+    EXPECT_TRUE(u.set(_constShared));
+    EXPECT_TRUE(u.find(&_shared)); // now _shared contains _constShared
+    EXPECT_EQ(NULL, _weak.promote().get()); // original _shared is now lost
+
+    EXPECT_TRUE((bool)_shared.get());
+    if (_shared.get()) {
+        EXPECT_EQ(1234, _shared->magic());
+    }
+    EXPECT_TRUE(u.clear());
+
+    // test that wp can be copied in
+    _weak = _shared;
+    EXPECT_TRUE(u.set(_weak));
+
+    _weak.clear();
+
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_TRUE(u.find(&_weak));
+    EXPECT_EQ(_weak.promote(), _shared);
+
+    // we can remove a weak pointer multiple times
+    _weak.clear();
+    EXPECT_TRUE(u.find(&_weak));
+    EXPECT_EQ(_weak.promote(), _shared);
+    EXPECT_TRUE(u.clear());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+};
+
+TEST_F(ADataTest, AData_RelaxedAndroidSpTest) {
+    int destructions = 0;
+    int _int = 0;
+    sp<EventCounter> _shared;
+    wp<EventCounter> _weak;
+    sp<RefBase> _shared_base;
+    wp<RefBase> _weak_base;
+    const sp<EventCounter> _constShared(new EventCounter(&destructions));
+
+    AData<int, sp<RefBase>, wp<RefBase>>::Relaxed<uint16_t> u;
+
+    // test that data is empty
+    EXPECT_FALSE(u.used());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // test that integer can be stored and read
+    EXPECT_TRUE(u.set<int>(1));
+    EXPECT_TRUE(u.used());
+    EXPECT_TRUE(u.find(&_int));
+    EXPECT_EQ(1, _int);
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // test that copiable & movable type (shared_ptr) is copied unless explicitly moved.
+    _shared = new EventCounter(&destructions, 234);
+    _weak = _shared; // used for tracking #234
+
+    EXPECT_TRUE(u.set(_shared));
+    EXPECT_TRUE((bool)_shared.get());
+    if (_shared.get()) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+
+    _shared.clear();
+    EXPECT_EQ(NULL, _shared.get());
+    EXPECT_NE(nullptr, _weak.promote().get()); // u still holds object
+
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_TRUE(u.find(&_shared)); // now u and _shared both hold object
+    EXPECT_TRUE((bool)_shared.get());
+    if (_shared.get()) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+    // verify the find did not move out object
+    _shared.clear();
+    EXPECT_EQ(NULL, _shared.get());
+    EXPECT_NE(nullptr, _weak.promote().get()); // u still holds object
+    EXPECT_TRUE(u.find(&_shared)); // now u and _shared both hold object
+    if (_shared.get()) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+
+    // verify that we can set object multiple times
+    EXPECT_TRUE(u.set(_shared));
+
+    // explicitly move in sp
+    EXPECT_TRUE(u.set(std::move(_shared)));
+    EXPECT_FALSE((bool)_shared.get()); // android also clears sp<> on move...
+    EXPECT_TRUE(u.find(&_shared)); // still can get it back
+    EXPECT_TRUE((bool)_shared.get());
+    if (_shared.get()) {
+        EXPECT_EQ(234, _shared->magic());
+    }
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    EXPECT_TRUE(u.used());
+    EXPECT_TRUE(u.clear()); // now only _shared contains the object
+    EXPECT_FALSE(u.used());
+
+    // we still hold a copy
+    EXPECT_TRUE((bool)_shared.get());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared)); // _shared still contains the object
+
+    EXPECT_TRUE(u.set(_constShared));
+    EXPECT_TRUE(u.find(&_shared)); // now _shared contains _constShared
+    EXPECT_EQ(NULL, _weak.promote().get()); // original _shared is now lost
+
+    EXPECT_TRUE((bool)_shared.get());
+    if (_shared.get()) {
+        EXPECT_EQ(1234, _shared->magic());
+    }
+    EXPECT_TRUE(u.clear());
+
+    // test that shared pointer can be set and removed as base type (but removed as derived only
+    // if it was set as derived type)
+    EXPECT_TRUE(u.set(_constShared));
+    EXPECT_TRUE(u.find(&_shared_base));
+    EXPECT_TRUE((bool)_shared_base.get());
+    if (_shared_base.get()) {
+        EXPECT_EQ(1234, static_cast<EventCounter*>(_shared_base.get())->magic());
+    }
+    _shared.clear();
+    EXPECT_TRUE(u.clear());
+    EXPECT_TRUE((bool)_shared_base.get());
+    if (_shared_base.get()) {
+        EXPECT_EQ(1234, static_cast<EventCounter*>(_shared_base.get())->magic());
+    }
+
+    EXPECT_TRUE(u.set(_shared_base)); // now u contains it as base class
+    EXPECT_TRUE((bool)_shared_base.get());
+    EXPECT_FALSE(u.find(&_shared)); // cannot get it as derived type
+    EXPECT_FALSE((bool)_shared.get());
+    _shared_base.clear();
+    EXPECT_TRUE(u.find(&_shared_base)); // can still get it as base type
+    EXPECT_TRUE((bool)_shared_base.get());
+    if (_shared_base.get()) {
+        EXPECT_EQ(1234, static_cast<EventCounter*>(_shared_base.get())->magic());
+    }
+    _shared = static_cast<DerivedCounter*>(_shared_base.get());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // test that wp can be copied in
+    _weak = _shared;
+    EXPECT_TRUE(u.set(_weak));
+
+    _weak.clear();
+
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_TRUE(u.find(&_weak));
+    EXPECT_EQ(_weak.promote(), _shared);
+
+    // we can remove a weak pointer multiple times
+    _weak.clear();
+    EXPECT_TRUE(u.find(&_weak));
+    EXPECT_EQ(_weak.promote(), _shared);
+    EXPECT_TRUE(u.clear());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    // test that weak pointer can be set and removed as base type (but removed as derived only
+    // if it was set as derived type)
+    _weak = _shared;
+    EXPECT_TRUE(u.set(_weak));
+    EXPECT_TRUE(u.find(&_weak_base));
+    EXPECT_TRUE(_weak_base.promote().get() == _shared.get());
+
+    _weak.clear();
+    _shared.clear();
+    EXPECT_TRUE(u.clear());
+    EXPECT_FALSE(u.find(&_int));
+    EXPECT_FALSE(u.find(&_shared));
+    EXPECT_FALSE(u.find(&_weak));
+    EXPECT_FALSE(u.find(&_shared_base));
+    EXPECT_FALSE(u.find(&_weak_base));
+
+    EXPECT_TRUE(u.set(_weak_base)); // now u_ also contains it as base class
+    EXPECT_FALSE(u.find(&_weak));   // cannot get it as derived type
+    EXPECT_FALSE(_weak.promote().get());
+    _weak_base.clear();
+    EXPECT_TRUE(u.find(&_weak_base)); // can still get it as base type
+    EXPECT_TRUE(_weak_base.promote().get());
+    if (_weak_base.promote().get()) {
+        EXPECT_EQ(1234, static_cast<EventCounter*>(_weak_base.promote().get())->magic());
+    }
+};
+
+} // namespace android
diff --git a/media/libstagefright/foundation/tests/Android.mk b/media/libstagefright/foundation/tests/Android.mk
index e7598ca..d741c6f 100644
--- a/media/libstagefright/foundation/tests/Android.mk
+++ b/media/libstagefright/foundation/tests/Android.mk
@@ -8,12 +8,14 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_SRC_FILES := \
+	AData_test.cpp \
 	Flagged_test.cpp \
 	TypeTraits_test.cpp \
 	Utils_test.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
 	libstagefright_foundation \
+	libutils \
 
 LOCAL_C_INCLUDES := \
 	frameworks/av/include \
diff --git a/media/libstagefright/foundation/tests/TypeTraits_test.cpp b/media/libstagefright/foundation/tests/TypeTraits_test.cpp
index 9fba435..1e2049d 100644
--- a/media/libstagefright/foundation/tests/TypeTraits_test.cpp
+++ b/media/libstagefright/foundation/tests/TypeTraits_test.cpp
@@ -32,6 +32,9 @@
 
 // =========== basic sanity tests for type-support templates
 TEST_F(TypeTraitsTest, StaticTests) {
+
+    // ============ is_integral_or_enum
+
     static_assert(!std::is_integral<A>::value, "enums should not be integral");
     static_assert(!std::is_integral<UA>::value, "enums should not be integral");
     static_assert(!std::is_integral<IA>::value, "enums should not be integral");
@@ -42,6 +45,8 @@
     static_assert(is_integral_or_enum<unsigned>::value, "unsigned ints should be integral_or_enum");
     static_assert(!is_integral_or_enum<float>::value, "floats should not be integral_or_enum");
 
+    // ============ is_unsigned_integral
+
     static_assert(!std::is_unsigned<UA>::value,
                   "unsigned enums should not be unsigned");
     static_assert(!std::is_unsigned<IA>::value,
@@ -61,6 +66,8 @@
     static_assert(!is_unsigned_integral<float>::value,
                   "floats should not be unsigned_integral");
 
+    // ============ is_signed_integral
+
     static_assert(!std::is_signed<UA>::value,
                   "unsigned enums should not be signed");
     static_assert(!std::is_signed<IA>::value,
@@ -80,6 +87,8 @@
     static_assert(!is_signed_integral<float>::value,
                   "floats should not be signed_integral");
 
+    // ============ underlying_integral_type
+
     static_assert(std::is_same<uint64_t, typename underlying_integral_type<uint64_t>::type>::value,
                   "underlying integral type of uint64_t should be uint64_t");
     static_assert(std::is_same<uint32_t, typename underlying_integral_type<UA>::type>::value,
@@ -91,6 +100,68 @@
     //typedef underlying_integral_type<float>::type no_type;
     static_assert(std::is_same<void, typename underlying_integral_type<float, void>::type>::value,
                   "underlying integral type of float cannot be specified");
+
+    // ============ is_one_of
+
+    static_assert(!is_one_of<int>::value, "int shouldn't be one of {}");
+    static_assert(!is_one_of<int, unsigned>::value, "int shouldn't be one of {unsigned}");
+    static_assert(!is_one_of<int, unsigned, float>::value,
+                  "int shouldn't be one of {unsigned, float}");
+    static_assert(is_one_of<int, int>::value, "int should be one of {int}");
+    static_assert(is_one_of<int, int, float>::value, "int should be one of {int, float}");
+    static_assert(is_one_of<int, float, int>::value, "int should be one of {float, int}");
+    static_assert(is_one_of<int, float, int, unsigned>::value,
+                  "int should be one of {float, int, unsigned}");
+    static_assert(is_one_of<int, float, unsigned, int>::value,
+                  "int should be one of {float, unsigned, int}");
+    static_assert(!is_one_of<int, int&>::value, "int shouldn't be one of {int&}");
+
+    // ============ are_unique
+
+    static_assert(are_unique<>::value, "{} should be unique");
+    static_assert(are_unique<int>::value, "{int} should be unique");
+    static_assert(are_unique<int, float>::value, "{int, float} should be unique");
+    static_assert(!are_unique<int, int>::value, "{int, int} shouldn't be unique");
+    static_assert(!are_unique<int, float, int>::value, "{int, float, int} shouldn't be unique");
+    static_assert(!are_unique<float, int, int>::value, "{float, int, int} shouldn't be unique");
+    static_assert(!are_unique<int, int, float>::value, "{int, int, float} shouldn't be unique");
+
+    // ============ find_first
+
+    static_assert(find_first<int>::index == 0, "int is not in {}");
+    static_assert(find_first<int, unsigned>::index == 0, "int is not in {unsigned}");
+    static_assert(find_first<int, unsigned, float>::index == 0, "int is not in {unsigned, float}");
+    static_assert(find_first<int, int>::index == 1, "int is 1st in {int}");
+    static_assert(find_first<int, int, float>::index == 1, "int is 1st in {int, float}");
+    static_assert(find_first<int, float, int>::index == 2, "int is 2nd in {float, int}");
+    static_assert(find_first<int, float, int, unsigned>::index == 2,
+                  "int is 2nd in {float, int, unsigned}");
+    static_assert(find_first<int, float, int, unsigned>::index == 2,
+                  "int is 2nd and 3rd in {float, int, int, unsigned}");
+    static_assert(find_first<int, float, unsigned, int>::index == 3,
+                  "int is 3rd in {float, unsigned, int}");
+    static_assert(find_first<int, int&>::index == 0, "int is not in {int&}");
+
+    // ============ find_first_convertible_to
+
+    static_assert(find_first_convertible_to<int>::index == 0, "int is not convertible to {}");
+    static_assert(find_first_convertible_to<int, unsigned*>::index == 0,
+                  "int is not convertible to {unsigned*}");
+    static_assert(find_first_convertible_to<int, unsigned*, float&>::index == 0,
+                  "int is not convertible to {unsigned, float&}");
+    static_assert(find_first_convertible_to<int, int>::index == 1, "int is convertible to {int}");
+    static_assert(find_first_convertible_to<int, unsigned, int>::index == 1,
+                  "int is convertible to 1st of {unsigned, int}");
+    static_assert(find_first_convertible_to<int, int&, float>::index == 2,
+                  "int is convertible to 2nd of {int&, float}");
+    static_assert(find_first_convertible_to<float, float*, int, unsigned>::index == 2,
+                  "float is convertible to 2nd of {float*, int, unsigned}");
+    static_assert(find_first_convertible_to<float, void, float[1], int>::index == 3,
+                  "int is 3rd convertible to {void, float[], int}");
+    static_assert(find_first_convertible_to<int&, const int&>::index == 1,
+                  "int& is convertible to {const int&}");
+    static_assert(find_first_convertible_to<const int&, int&>::index == 0,
+                  "const int& is not convertible to {int&}");
 }
 
 } // namespace android
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index 93e9a4b..ef55620 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -89,6 +89,8 @@
     // Add a SynPoint derived from |event|.
     void addSyncPoint_l(const ATSParser::SyncEvent &event);
 
+    status_t  estimateDurationsFromTimesUsAtEnd();
+
     DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor);
 };
 
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 9edd0de..5b22a2f 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -21,12 +21,15 @@
 #include <utils/threads.h>
 #include <utils/KeyedVector.h>
 
+#include "OmxNodeOwner.h"
+
 namespace android {
 
 struct OMXMaster;
 struct OMXNodeInstance;
 
 class OMX : public BnOMX,
+            public OmxNodeOwner,
             public IBinder::DeathRecipient {
 public:
     OMX();
@@ -43,7 +46,7 @@
 
     virtual void binderDied(const wp<IBinder> &the_late_who);
 
-    status_t freeNode(const sp<OMXNodeInstance>& instance);
+    virtual status_t freeNode(const sp<OMXNodeInstance>& instance);
 
 protected:
     virtual ~OMX();
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index ab12a86..ca24c2f 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -18,11 +18,12 @@
 
 #define OMX_NODE_INSTANCE_H_
 
-#include "OMX.h"
-
+#include <media/IOMX.h>
 #include <utils/RefBase.h>
-#include <utils/SortedVector.h>
 #include <utils/threads.h>
+#include <utils/KeyedVector.h>
+#include <utils/SortedVector.h>
+#include "OmxNodeOwner.h"
 
 namespace android {
 class IOMXBufferSource;
@@ -32,7 +33,7 @@
 
 struct OMXNodeInstance : public BnOMXNode {
     OMXNodeInstance(
-            OMX *owner, const sp<IOMXObserver> &observer, const char *name);
+            OmxNodeOwner *owner, const sp<IOMXObserver> &observer, const char *name);
 
     void setHandle(OMX_HANDLETYPE handle);
 
@@ -66,7 +67,7 @@
             const sp<IOMXBufferSource> &bufferSource);
 
     status_t allocateSecureBuffer(
-            OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
+            OMX_U32 portIndex, size_t size, IOMX::buffer_id *buffer,
             void **buffer_data, sp<NativeHandle> *native_handle);
 
     status_t useBuffer(
@@ -106,7 +107,7 @@
 
     Mutex mLock;
 
-    OMX *mOwner;
+    OmxNodeOwner *mOwner;
     OMX_HANDLETYPE mHandle;
     sp<IOMXObserver> mObserver;
     sp<CallbackDispatcher> mDispatcher;
@@ -125,14 +126,14 @@
 
     struct ActiveBuffer {
         OMX_U32 mPortIndex;
-        OMX::buffer_id mID;
+        IOMX::buffer_id mID;
     };
     Vector<ActiveBuffer> mActiveBuffers;
     // for buffer ptr to buffer id translation
     Mutex mBufferIDLock;
     uint32_t mBufferIDCount;
-    KeyedVector<OMX::buffer_id, OMX_BUFFERHEADERTYPE *> mBufferIDToBufferHeader;
-    KeyedVector<OMX_BUFFERHEADERTYPE *, OMX::buffer_id> mBufferHeaderToBufferID;
+    KeyedVector<IOMX::buffer_id, OMX_BUFFERHEADERTYPE *> mBufferIDToBufferHeader;
+    KeyedVector<OMX_BUFFERHEADERTYPE *, IOMX::buffer_id> mBufferHeaderToBufferID;
 
     bool mLegacyAdaptiveExperiment;
     IOMX::PortMode mPortMode[2];
@@ -167,45 +168,45 @@
 
     ~OMXNodeInstance();
 
-    void addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id);
-    void removeActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id);
+    void addActiveBuffer(OMX_U32 portIndex, IOMX::buffer_id id);
+    void removeActiveBuffer(OMX_U32 portIndex, IOMX::buffer_id id);
     void freeActiveBuffers();
 
     // For buffer id management
-    OMX::buffer_id makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
-    OMX_BUFFERHEADERTYPE *findBufferHeader(OMX::buffer_id buffer, OMX_U32 portIndex);
-    OMX::buffer_id findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
-    void invalidateBufferID(OMX::buffer_id buffer);
+    IOMX::buffer_id makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
+    OMX_BUFFERHEADERTYPE *findBufferHeader(IOMX::buffer_id buffer, OMX_U32 portIndex);
+    IOMX::buffer_id findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader);
+    void invalidateBufferID(IOMX::buffer_id buffer);
 
     bool isProhibitedIndex_l(OMX_INDEXTYPE index);
 
     status_t useBuffer_l(
             OMX_U32 portIndex, const sp<IMemory> &params,
-            OMX::buffer_id *buffer);
+            IOMX::buffer_id *buffer);
 
     status_t useGraphicBuffer_l(
             OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
-            OMX::buffer_id *buffer);
+            IOMX::buffer_id *buffer);
 
     status_t useGraphicBufferWithMetadata_l(
             OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
-            OMX::buffer_id *buffer);
+            IOMX::buffer_id *buffer);
 
     status_t useGraphicBuffer2_l(
             OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
-            OMX::buffer_id *buffer);
+            IOMX::buffer_id *buffer);
 
     status_t emptyBuffer_l(
-            OMX::buffer_id buffer,
+            IOMX::buffer_id buffer,
             OMX_U32 rangeOffset, OMX_U32 rangeLength,
             OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
 
     status_t emptyGraphicBuffer_l(
-            OMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
+            IOMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
             OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
 
     status_t emptyNativeHandleBuffer_l(
-            OMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
+            IOMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
             OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
 
     status_t emptyBuffer_l(
@@ -252,11 +253,11 @@
     // buffer.)
     status_t updateGraphicBufferInMeta_l(
             OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
-            OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
+            IOMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
 
     status_t updateNativeHandleInMeta_l(
             OMX_U32 portIndex, const sp<NativeHandle> &nativeHandle,
-            OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
+            IOMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header);
 
     sp<IOMXBufferSource> getBufferSource();
     void setBufferSource(const sp<IOMXBufferSource> &bufferSource);
diff --git a/media/libstagefright/include/OmxNodeOwner.h b/media/libstagefright/include/OmxNodeOwner.h
new file mode 100644
index 0000000..64ec7f7
--- /dev/null
+++ b/media/libstagefright/include/OmxNodeOwner.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OMX_NODE_OWNER_H_
+
+#define OMX_NODE_OWNER_H_
+
+namespace android {
+
+struct OMXNodeInstance;
+
+/**
+ * This struct is needed to separate OMX from OMXNodeInstance.
+ *
+ * TODO: This might not be needed after Treble transition is complete.
+ */
+struct OmxNodeOwner {
+    virtual status_t freeNode(const sp<OMXNodeInstance> &instance) = 0;
+    virtual ~OmxNodeOwner() {}
+};
+
+}
+
+#endif  // OMX_NODE_OWNER_H_
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 844479e..4975d9a 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -90,6 +90,10 @@
         return mParser->mFlags;
     }
 
+    uint64_t firstPTS() const {
+        return mFirstPTS;
+    }
+
 private:
     struct StreamInfo {
         unsigned mType;
@@ -135,6 +139,7 @@
 
     void signalEOS(status_t finalResult);
 
+    SourceType getSourceType();
     sp<MediaSource> getSource(SourceType type);
 
     bool isAudio() const;
@@ -208,11 +213,12 @@
     : mHasReturnedData(false), mOffset(offset), mTimeUs(0) {}
 
 void ATSParser::SyncEvent::init(off64_t offset, const sp<MediaSource> &source,
-        int64_t timeUs) {
+        int64_t timeUs, SourceType type) {
     mHasReturnedData = true;
     mOffset = offset;
     mMediaSource = source;
     mTimeUs = timeUs;
+    mType = type;
 }
 
 void ATSParser::SyncEvent::reset() {
@@ -1121,13 +1127,24 @@
                 int64_t timeUs;
                 if (accessUnit->meta()->findInt64("timeUs", &timeUs)) {
                     found = true;
-                    event->init(pesStartOffset, mSource, timeUs);
+                    event->init(pesStartOffset, mSource, timeUs, getSourceType());
                 }
             }
         }
     }
 }
 
+ATSParser::SourceType ATSParser::Stream::getSourceType() {
+    if (isVideo()) {
+        return VIDEO;
+    } else if (isAudio()) {
+        return AUDIO;
+    } else if (isMeta()) {
+        return META;
+    }
+    return NUM_SOURCE_TYPES;
+}
+
 sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
     switch (type) {
         case VIDEO:
@@ -1565,6 +1582,16 @@
     return mPrograms.editItemAt(0)->PTSTimeDeltaEstablished();
 }
 
+int64_t ATSParser::getFirstPTSTimeUs() {
+    for (size_t i = 0; i < mPrograms.size(); ++i) {
+        sp<ATSParser::Program> program = mPrograms.itemAt(i);
+        if (program->PTSTimeDeltaEstablished()) {
+            return (program->firstPTS() * 100) / 9;
+        }
+    }
+    return -1;
+}
+
 __attribute__((no_sanitize("integer")))
 void ATSParser::updatePCR(
         unsigned /* PID */, uint64_t PCR, uint64_t byteOffsetFromStart) {
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 2b166f0..faae6c9 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -62,18 +62,26 @@
         ALIGNED_VIDEO_DATA         = 2,
     };
 
+    enum SourceType {
+        VIDEO = 0,
+        AUDIO = 1,
+        META  = 2,
+        NUM_SOURCE_TYPES = 3
+    };
+
     // Event is used to signal sync point event at feedTSPacket().
     struct SyncEvent {
         explicit SyncEvent(off64_t offset);
 
         void init(off64_t offset, const sp<MediaSource> &source,
-                int64_t timeUs);
+                int64_t timeUs, SourceType type);
 
         bool hasReturnedData() const { return mHasReturnedData; }
         void reset();
         off64_t getOffset() const { return mOffset; }
         const sp<MediaSource> &getMediaSource() const { return mMediaSource; }
         int64_t getTimeUs() const { return mTimeUs; }
+        SourceType getType() const { return mType; }
 
     private:
         bool mHasReturnedData;
@@ -87,6 +95,7 @@
         sp<MediaSource> mMediaSource;
         /* The timestamp of the sync frame. */
         int64_t mTimeUs;
+        SourceType mType;
     };
 
     explicit ATSParser(uint32_t flags = 0);
@@ -107,17 +116,13 @@
 
     void signalEOS(status_t finalResult);
 
-    enum SourceType {
-        VIDEO = 0,
-        AUDIO = 1,
-        META  = 2,
-        NUM_SOURCE_TYPES = 3
-    };
     sp<MediaSource> getSource(SourceType type);
     bool hasSource(SourceType type) const;
 
     bool PTSTimeDeltaEstablished();
 
+    int64_t getFirstPTSTimeUs();
+
     enum {
         // From ISO/IEC 13818-1: 2000 (E), Table 2-29
         STREAMTYPE_RESERVED             = 0x00,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 4fcf7b5..548f44e 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -44,6 +44,7 @@
       mEnabled(true),
       mFormat(NULL),
       mLastQueuedTimeUs(0),
+      mEstimatedBufferDurationUs(-1),
       mEOSResult(OK),
       mLatestEnqueuedMeta(NULL),
       mLatestDequeuedMeta(NULL) {
@@ -309,6 +310,8 @@
 
     mFormat = NULL;
     mLatestEnqueuedMeta = NULL;
+
+    mEstimatedBufferDurationUs = -1;
 }
 
 void AnotherPacketSource::queueDiscontinuity(
@@ -431,6 +434,31 @@
     return durationUs;
 }
 
+int64_t AnotherPacketSource::getEstimatedBufferDurationUs() {
+    Mutex::Autolock autoLock(mLock);
+    if (mEstimatedBufferDurationUs >= 0) {
+        return mEstimatedBufferDurationUs;
+    }
+
+    SortedVector<int64_t> maxTimesUs;
+    List<sp<ABuffer> >::iterator it;
+    int64_t t1 = 0, t2 = 0;
+    for (it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+        int64_t timeUs = 0;
+        const sp<ABuffer> &buffer = *it;
+        if (!buffer->meta()->findInt64("timeUs", &timeUs)) {
+            continue;
+        }
+        maxTimesUs.add(timeUs);
+        while (maxTimesUs.size() > 2) {
+            maxTimesUs.removeAt(0);
+            t1 = maxTimesUs.itemAt(0);
+            t2 = maxTimesUs.itemAt(1);
+        }
+    }
+    return mEstimatedBufferDurationUs = t2 - t1;
+}
+
 status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) {
     *timeUs = 0;
 
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index dd6849e..b0890d7 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -57,6 +57,9 @@
     // presentation timestamps since the last discontinuity (if any).
     int64_t getBufferedDurationUs(status_t *finalResult);
 
+    // Returns the difference between the two largest timestamps queued
+    int64_t getEstimatedBufferDurationUs();
+
     status_t nextBufferTime(int64_t *timeUs);
 
     void queueAccessUnit(const sp<ABuffer> &buffer);
@@ -113,6 +116,7 @@
     bool mEnabled;
     sp<MetaData> mFormat;
     int64_t mLastQueuedTimeUs;
+    int64_t mEstimatedBufferDurationUs;
     List<sp<ABuffer> > mBuffers;
     status_t mEOSResult;
     sp<AMessage> mLatestEnqueuedMeta;
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 116a5bc..bde33dc 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -26,6 +26,7 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
@@ -40,6 +41,8 @@
 namespace android {
 
 static const size_t kTSPacketSize = 188;
+static const int kMaxDurationReadSize = 250000LL;
+static const int kMaxDurationRetry = 6;
 
 struct MPEG2TSSource : public MediaSource {
     MPEG2TSSource(
@@ -243,6 +246,8 @@
             const sp<MetaData> meta = impl->getFormat();
             meta->setInt64(kKeyDuration, durationUs);
             impl->setFormat(meta);
+        } else {
+            estimateDurationsFromTimesUsAtEnd();
         }
     }
 
@@ -301,6 +306,106 @@
     }
 }
 
+status_t MPEG2TSExtractor::estimateDurationsFromTimesUsAtEnd()  {
+    if (!(mDataSource->flags() & DataSource::kIsLocalFileSource)) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    off64_t size = 0;
+    status_t err = mDataSource->getSize(&size);
+    if (err != OK) {
+        return err;
+    }
+
+    uint8_t packet[kTSPacketSize];
+    const off64_t zero = 0;
+    off64_t offset = max(zero, size - kMaxDurationReadSize);
+    if (mDataSource->readAt(offset, &packet, 0) < 0) {
+        return ERROR_IO;
+    }
+
+    int retry = 0;
+    bool allDurationsFound = false;
+    int64_t timeAnchorUs = mParser->getFirstPTSTimeUs();
+    do {
+        int bytesRead = 0;
+        sp<ATSParser> parser = new ATSParser(ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE);
+        ATSParser::SyncEvent ev(0);
+        offset = max(zero, size - (kMaxDurationReadSize << retry));
+        offset = (offset / kTSPacketSize) * kTSPacketSize;
+        for (;;) {
+            if (bytesRead >= kMaxDurationReadSize << max(0, retry - 1)) {
+                break;
+            }
+
+            ssize_t n = mDataSource->readAt(offset, packet, kTSPacketSize);
+            if (n < 0) {
+                return n;
+            } else if (n < (ssize_t)kTSPacketSize) {
+                break;
+            }
+
+            offset += kTSPacketSize;
+            bytesRead += kTSPacketSize;
+            err = parser->feedTSPacket(packet, kTSPacketSize, &ev);
+            if (err != OK) {
+                return err;
+            }
+
+            if (ev.hasReturnedData()) {
+                int64_t durationUs = ev.getTimeUs();
+                ATSParser::SourceType type = ev.getType();
+                ev.reset();
+
+                int64_t firstTimeUs;
+                sp<AnotherPacketSource> src =
+                    (AnotherPacketSource *)mParser->getSource(type).get();
+                if (src == NULL || src->nextBufferTime(&firstTimeUs) != OK) {
+                    continue;
+                }
+                durationUs += src->getEstimatedBufferDurationUs();
+                durationUs -= timeAnchorUs;
+                durationUs -= firstTimeUs;
+                if (durationUs > 0) {
+                    int64_t origDurationUs, lastDurationUs;
+                    const sp<MetaData> meta = src->getFormat();
+                    const uint32_t kKeyLastDuration = 'ldur';
+                    // Require two consecutive duration calculations to be within 1 sec before
+                    // updating; use MetaData to store previous duration estimate in per-stream
+                    // context.
+                    if (!meta->findInt64(kKeyDuration, &origDurationUs)
+                            || !meta->findInt64(kKeyLastDuration, &lastDurationUs)
+                            || (origDurationUs < durationUs
+                             && abs(durationUs - lastDurationUs) < 60000000)) {
+                        meta->setInt64(kKeyDuration, durationUs);
+                    }
+                    meta->setInt64(kKeyLastDuration, durationUs);
+                }
+            }
+        }
+
+        if (!allDurationsFound) {
+            allDurationsFound = true;
+            for (auto t: {ATSParser::VIDEO, ATSParser::AUDIO}) {
+                sp<AnotherPacketSource> src = (AnotherPacketSource *)mParser->getSource(t).get();
+                if (src == NULL) {
+                    continue;
+                }
+                int64_t durationUs;
+                const sp<MetaData> meta = src->getFormat();
+                if (!meta->findInt64(kKeyDuration, &durationUs)) {
+                    allDurationsFound = false;
+                    break;
+                }
+            }
+        }
+
+        ++retry;
+    } while(!allDurationsFound && offset > 0 && retry <= kMaxDurationRetry);
+
+    return allDurationsFound? OK : ERROR_UNSUPPORTED;
+}
+
 uint32_t MPEG2TSExtractor::flags() const {
     return CAN_PAUSE | CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD;
 }
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index be4a932..c20e9fc 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -332,7 +332,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 OMXNodeInstance::OMXNodeInstance(
-        OMX *owner, const sp<IOMXObserver> &observer, const char *name)
+        OmxNodeOwner *owner, const sp<IOMXObserver> &observer, const char *name)
     : mOwner(owner),
       mHandle(NULL),
       mObserver(observer),
@@ -799,9 +799,7 @@
         if (!graphic) {
             // Extension not supported, check for manual override with system property
             // This is a temporary workaround until partners support the OMX extension
-            char value[PROPERTY_VALUE_MAX];
-            if (property_get("media.mediadrmservice.enable", value, NULL)
-                && (!strcmp("1", value) || !strcasecmp("true", value))) {
+            if (property_get_bool("media.mediadrmservice.enable", false)) {
                 CLOG_CONFIG(enableNativeBuffers, "system property override: using native-handles");
                 mSecureBufferType[portIndex] = kSecureBufferTypeNativeHandle;
             } else if (mSecureBufferType[portIndex] == kSecureBufferTypeUnknown) {
@@ -1010,7 +1008,7 @@
 }
 
 status_t OMXNodeInstance::useBuffer(
-        OMX_U32 portIndex, const OMXBuffer &omxBuffer, OMX::buffer_id *buffer) {
+        OMX_U32 portIndex, const OMXBuffer &omxBuffer, IOMX::buffer_id *buffer) {
     if (buffer == NULL) {
         ALOGE("b/25884056");
         return BAD_VALUE;
@@ -1040,7 +1038,7 @@
 }
 
 status_t OMXNodeInstance::useBuffer_l(
-        OMX_U32 portIndex, const sp<IMemory> &params, OMX::buffer_id *buffer) {
+        OMX_U32 portIndex, const sp<IMemory> &params, IOMX::buffer_id *buffer) {
     BufferMeta *buffer_meta;
     OMX_BUFFERHEADERTYPE *header;
     OMX_ERRORTYPE err = OMX_ErrorNone;
@@ -1147,7 +1145,7 @@
 
 status_t OMXNodeInstance::useGraphicBuffer2_l(
         OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
-        OMX::buffer_id *buffer) {
+        IOMX::buffer_id *buffer) {
     if (graphicBuffer == NULL || buffer == NULL) {
         ALOGE("b/25884056");
         return BAD_VALUE;
@@ -1203,7 +1201,7 @@
 // can be renamed to useGraphicBuffer.
 status_t OMXNodeInstance::useGraphicBuffer_l(
         OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
-        OMX::buffer_id *buffer) {
+        IOMX::buffer_id *buffer) {
     if (graphicBuffer == NULL || buffer == NULL) {
         ALOGE("b/25884056");
         return BAD_VALUE;
@@ -1273,7 +1271,7 @@
 
 status_t OMXNodeInstance::useGraphicBufferWithMetadata_l(
         OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
-        OMX::buffer_id *buffer) {
+        IOMX::buffer_id *buffer) {
     if (portIndex != kPortIndexOutput) {
         return BAD_VALUE;
     }
@@ -1296,7 +1294,7 @@
 
 status_t OMXNodeInstance::updateGraphicBufferInMeta_l(
         OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
-        OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
+        IOMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
     // No need to check |graphicBuffer| since NULL is valid for it as below.
     if (header == NULL) {
         ALOGE("b/25884056");
@@ -1336,7 +1334,7 @@
 
 status_t OMXNodeInstance::updateNativeHandleInMeta_l(
         OMX_U32 portIndex, const sp<NativeHandle>& nativeHandle,
-        OMX::buffer_id buffer, OMX_BUFFERHEADERTYPE *header) {
+        IOMX::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");
@@ -1423,7 +1421,7 @@
 }
 
 status_t OMXNodeInstance::allocateSecureBuffer(
-        OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
+        OMX_U32 portIndex, size_t size, IOMX::buffer_id *buffer,
         void **buffer_data, sp<NativeHandle> *native_handle) {
     if (buffer == NULL || buffer_data == NULL || native_handle == NULL) {
         ALOGE("b/25884056");
@@ -1481,7 +1479,7 @@
 }
 
 status_t OMXNodeInstance::freeBuffer(
-        OMX_U32 portIndex, OMX::buffer_id buffer) {
+        OMX_U32 portIndex, IOMX::buffer_id buffer) {
     Mutex::Autolock autoLock(mLock);
     CLOG_BUFFER(freeBuffer, "%s:%u %#x", portString(portIndex), portIndex, buffer);
 
@@ -1505,7 +1503,7 @@
 }
 
 status_t OMXNodeInstance::fillBuffer(
-        OMX::buffer_id buffer, const OMXBuffer &omxBuffer, int fenceFd) {
+        IOMX::buffer_id buffer, const OMXBuffer &omxBuffer, int fenceFd) {
     Mutex::Autolock autoLock(mLock);
 
     OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexOutput);
@@ -1579,7 +1577,7 @@
 }
 
 status_t OMXNodeInstance::emptyBuffer_l(
-        OMX::buffer_id buffer,
+        IOMX::buffer_id buffer,
         OMX_U32 rangeOffset, OMX_U32 rangeLength,
         OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
 
@@ -1731,7 +1729,7 @@
 
 // like emptyBuffer, but the data is already in header->pBuffer
 status_t OMXNodeInstance::emptyGraphicBuffer_l(
-        OMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
+        IOMX::buffer_id buffer, const sp<GraphicBuffer> &graphicBuffer,
         OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
     OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
     if (header == NULL) {
@@ -1803,7 +1801,7 @@
 }
 
 status_t OMXNodeInstance::emptyNativeHandleBuffer_l(
-        OMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
+        IOMX::buffer_id buffer, const sp<NativeHandle> &nativeHandle,
         OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
     OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer, kPortIndexInput);
     if (header == NULL) {
@@ -2199,7 +2197,7 @@
     return OMX_ErrorNone;
 }
 
-void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id) {
+void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, IOMX::buffer_id id) {
     ActiveBuffer active;
     active.mPortIndex = portIndex;
     active.mID = id;
@@ -2211,7 +2209,7 @@
 }
 
 void OMXNodeInstance::removeActiveBuffer(
-        OMX_U32 portIndex, OMX::buffer_id id) {
+        OMX_U32 portIndex, IOMX::buffer_id id) {
     for (size_t i = 0; i < mActiveBuffers.size(); ++i) {
         if (mActiveBuffers[i].mPortIndex == portIndex
                 && mActiveBuffers[i].mID == id) {
@@ -2236,17 +2234,17 @@
     }
 }
 
-OMX::buffer_id OMXNodeInstance::makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
+IOMX::buffer_id OMXNodeInstance::makeBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
     if (bufferHeader == NULL) {
         return 0;
     }
     Mutex::Autolock autoLock(mBufferIDLock);
-    OMX::buffer_id buffer;
+    IOMX::buffer_id buffer;
     do { // handle the very unlikely case of ID overflow
         if (++mBufferIDCount == 0) {
             ++mBufferIDCount;
         }
-        buffer = (OMX::buffer_id)mBufferIDCount;
+        buffer = (IOMX::buffer_id)mBufferIDCount;
     } while (mBufferIDToBufferHeader.indexOfKey(buffer) >= 0);
     mBufferIDToBufferHeader.add(buffer, bufferHeader);
     mBufferHeaderToBufferID.add(bufferHeader, buffer);
@@ -2254,7 +2252,7 @@
 }
 
 OMX_BUFFERHEADERTYPE *OMXNodeInstance::findBufferHeader(
-        OMX::buffer_id buffer, OMX_U32 portIndex) {
+        IOMX::buffer_id buffer, OMX_U32 portIndex) {
     if (buffer == 0) {
         return NULL;
     }
@@ -2275,7 +2273,7 @@
     return header;
 }
 
-OMX::buffer_id OMXNodeInstance::findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
+IOMX::buffer_id OMXNodeInstance::findBufferID(OMX_BUFFERHEADERTYPE *bufferHeader) {
     if (bufferHeader == NULL) {
         return 0;
     }
@@ -2288,7 +2286,7 @@
     return mBufferHeaderToBufferID.valueAt(index);
 }
 
-void OMXNodeInstance::invalidateBufferID(OMX::buffer_id buffer) {
+void OMXNodeInstance::invalidateBufferID(IOMX::buffer_id buffer) {
     if (buffer == 0) {
         return;
     }
diff --git a/media/libstagefright/omx/hal/1.0/Android.mk b/media/libstagefright/omx/hal/1.0/Android.mk
new file mode 100644
index 0000000..b84d74b
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := android.hardware.media.omx@1.0-impl
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := \
+    GraphicBufferSource.cpp \
+    Omx.cpp \
+    OmxBufferSource.cpp \
+    OmxNode.cpp \
+    OmxObserver.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
+    libutils \
+    android.hardware.media.omx@1.0 \
+    android.hardware.graphics.common@1.0 \
+    android.hardware.media@1.0 \
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/omx/hal/1.0/GraphicBufferSource.cpp b/media/libstagefright/omx/hal/1.0/GraphicBufferSource.cpp
new file mode 100644
index 0000000..6a43883
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/GraphicBufferSource.cpp
@@ -0,0 +1,66 @@
+#include "GraphicBufferSource.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::omx::V1_0::IGraphicBufferSource follow.
+Return<Status> GraphicBufferSource::configure(const sp<IOmxNode>& omxNode, Dataspace dataspace) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setSuspend(bool suspend) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setMaxFps(float maxFps) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setStartTimeUs(int64_t startTimeUs) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setColorAspects(const ColorAspects& aspects) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::setTimeOffsetUs(int64_t timeOffsetUs) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> GraphicBufferSource::signalEndOfInputStream() {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+
+IGraphicBufferSource* HIDL_FETCH_IGraphicBufferSource(const char* /* name */) {
+    return new GraphicBufferSource();
+}
+
+} // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/GraphicBufferSource.h b/media/libstagefright/omx/hal/1.0/GraphicBufferSource.h
new file mode 100644
index 0000000..fb8c89d
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/GraphicBufferSource.h
@@ -0,0 +1,50 @@
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__GRAPHICBUFFERSOURCE_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__GRAPHICBUFFERSOURCE_H
+
+#include <android/hardware/media/omx/1.0/IGraphicBufferSource.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::graphics::common::V1_0::Dataspace;
+using ::android::hardware::media::omx::V1_0::ColorAspects;
+using ::android::hardware::media::omx::V1_0::IGraphicBufferSource;
+using ::android::hardware::media::omx::V1_0::IOmxNode;
+using ::android::hardware::media::omx::V1_0::Status;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct GraphicBufferSource : public IGraphicBufferSource {
+    // Methods from ::android::hardware::media::omx::V1_0::IGraphicBufferSource follow.
+    Return<Status> configure(const sp<IOmxNode>& omxNode, Dataspace dataspace) override;
+    Return<Status> setSuspend(bool suspend) override;
+    Return<Status> setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
+    Return<Status> setMaxFps(float maxFps) override;
+    Return<Status> setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+    Return<Status> setStartTimeUs(int64_t startTimeUs) override;
+    Return<Status> setColorAspects(const ColorAspects& aspects) override;
+    Return<Status> setTimeOffsetUs(int64_t timeOffsetUs) override;
+    Return<Status> signalEndOfInputStream() override;
+
+};
+
+extern "C" IGraphicBufferSource* HIDL_FETCH_IGraphicBufferSource(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_OMX_V1_0__GRAPHICBUFFERSOURCE_H
diff --git a/media/libstagefright/omx/hal/1.0/Omx.cpp b/media/libstagefright/omx/hal/1.0/Omx.cpp
new file mode 100644
index 0000000..68040eb
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/Omx.cpp
@@ -0,0 +1,31 @@
+#include "Omx.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::omx::V1_0::IOmx follow.
+Return<void> Omx::listNodes(listNodes_cb _hidl_cb) {
+    // TODO implement
+    return Void();
+}
+
+Return<void> Omx::allocateNode(const hidl_string& name, const sp<IOmxObserver>& observer, allocateNode_cb _hidl_cb) {
+    // TODO implement
+    return Void();
+}
+
+
+IOmx* HIDL_FETCH_IOmx(const char* /* name */) {
+    return new Omx();
+}
+
+} // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/Omx.h b/media/libstagefright/omx/hal/1.0/Omx.h
new file mode 100644
index 0000000..02a0a01
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/Omx.h
@@ -0,0 +1,42 @@
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMX_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMX_H
+
+#include <android/hardware/media/omx/1.0/IOmx.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::omx::V1_0::IOmx;
+using ::android::hardware::media::omx::V1_0::IOmxNode;
+using ::android::hardware::media::omx::V1_0::IOmxObserver;
+using ::android::hardware::media::omx::V1_0::Status;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct Omx : public IOmx {
+    // Methods from ::android::hardware::media::omx::V1_0::IOmx follow.
+    Return<void> listNodes(listNodes_cb _hidl_cb) override;
+    Return<void> allocateNode(const hidl_string& name, const sp<IOmxObserver>& observer, allocateNode_cb _hidl_cb) override;
+
+};
+
+extern "C" IOmx* HIDL_FETCH_IOmx(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMX_H
diff --git a/media/libstagefright/omx/hal/1.0/OmxBufferSource.cpp b/media/libstagefright/omx/hal/1.0/OmxBufferSource.cpp
new file mode 100644
index 0000000..2885d0d
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxBufferSource.cpp
@@ -0,0 +1,46 @@
+#include "OmxBufferSource.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::omx::V1_0::IOmxBufferSource follow.
+Return<void> OmxBufferSource::onOmxExecuting() {
+    // TODO implement
+    return Void();
+}
+
+Return<void> OmxBufferSource::onOmxIdle() {
+    // TODO implement
+    return Void();
+}
+
+Return<void> OmxBufferSource::onOmxLoaded() {
+    // TODO implement
+    return Void();
+}
+
+Return<void> OmxBufferSource::onInputBufferAdded(uint32_t buffer) {
+    // TODO implement
+    return Void();
+}
+
+Return<void> OmxBufferSource::onInputBufferEmptied(uint32_t buffer, const hidl_handle& fence) {
+    // TODO implement
+    return Void();
+}
+
+
+IOmxBufferSource* HIDL_FETCH_IOmxBufferSource(const char* /* name */) {
+    return new OmxBufferSource();
+}
+
+} // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/OmxBufferSource.h b/media/libstagefright/omx/hal/1.0/OmxBufferSource.h
new file mode 100644
index 0000000..5e4855b
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxBufferSource.h
@@ -0,0 +1,42 @@
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXBUFFERSOURCE_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXBUFFERSOURCE_H
+
+#include <android/hardware/media/omx/1.0/IOmxBufferSource.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::omx::V1_0::IOmxBufferSource;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct OmxBufferSource : public IOmxBufferSource {
+    // Methods from ::android::hardware::media::omx::V1_0::IOmxBufferSource follow.
+    Return<void> onOmxExecuting() override;
+    Return<void> onOmxIdle() override;
+    Return<void> onOmxLoaded() override;
+    Return<void> onInputBufferAdded(uint32_t buffer) override;
+    Return<void> onInputBufferEmptied(uint32_t buffer, const hidl_handle& fence) override;
+
+};
+
+extern "C" IOmxBufferSource* HIDL_FETCH_IOmxBufferSource(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXBUFFERSOURCE_H
diff --git a/media/libstagefright/omx/hal/1.0/OmxNode.cpp b/media/libstagefright/omx/hal/1.0/OmxNode.cpp
new file mode 100644
index 0000000..ba0e722
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxNode.cpp
@@ -0,0 +1,111 @@
+#include "OmxNode.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::omx::V1_0::IOmxNode follow.
+Return<Status> OmxNode::freeNode() {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> OmxNode::sendCommand(uint32_t cmd, const hidl_vec<uint8_t>& info) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<void> OmxNode::getParameter(uint32_t index, const hidl_vec<uint8_t>& inParams, getParameter_cb _hidl_cb) {
+    // TODO implement
+    return Void();
+}
+
+Return<Status> OmxNode::setParameter(uint32_t index, const hidl_vec<uint8_t>& params) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<void> OmxNode::getConfig(uint32_t index, const hidl_vec<uint8_t>& inConfig, getConfig_cb _hidl_cb) {
+    // TODO implement
+    return Void();
+}
+
+Return<Status> OmxNode::setConfig(uint32_t index, const hidl_vec<uint8_t>& config) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> OmxNode::setPortMode(uint32_t portIndex, PortMode mode) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> OmxNode::prepareForAdaptivePlayback(uint32_t portIndex, bool enable, uint32_t maxFrameWidth, uint32_t maxFrameHeight) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<void> OmxNode::configureVideoTunnelMode(uint32_t portIndex, bool tunneled, uint32_t audioHwSync, configureVideoTunnelMode_cb _hidl_cb) {
+    // TODO implement
+    return Void();
+}
+
+Return<void> OmxNode::getGraphicBufferUsage(uint32_t portIndex, getGraphicBufferUsage_cb _hidl_cb) {
+    // TODO implement
+    return Void();
+}
+
+Return<Status> OmxNode::setInputSurface(const sp<IOmxBufferSource>& bufferSource) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<void> OmxNode::allocateSecureBuffer(uint32_t portIndex, uint64_t size, allocateSecureBuffer_cb _hidl_cb) {
+    // TODO implement
+    return Void();
+}
+
+Return<void> OmxNode::useBuffer(uint32_t portIndex, const CodecBuffer& omxBuffer, useBuffer_cb _hidl_cb) {
+    // TODO implement
+    return Void();
+}
+
+Return<Status> OmxNode::freeBuffer(uint32_t portIndex, uint32_t buffer) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> OmxNode::fillBuffer(uint32_t buffer, const CodecBuffer& omxBuffer, const hidl_handle& fence) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<Status> OmxNode::emptyBuffer(uint32_t buffer, const CodecBuffer& omxBuffer, uint32_t flags, uint64_t timestampUs, const hidl_handle& fence) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+Return<void> OmxNode::getExtensionIndex(const hidl_string& parameterName, getExtensionIndex_cb _hidl_cb) {
+    // TODO implement
+    return Void();
+}
+
+Return<Status> OmxNode::dispatchMessage(const Message& msg) {
+    // TODO implement
+    return ::android::hardware::media::omx::V1_0::Status {};
+}
+
+
+IOmxNode* HIDL_FETCH_IOmxNode(const char* /* name */) {
+    return new OmxNode();
+}
+
+} // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/OmxNode.h b/media/libstagefright/omx/hal/1.0/OmxNode.h
new file mode 100644
index 0000000..dd9e5b4
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxNode.h
@@ -0,0 +1,60 @@
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXNODE_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXNODE_H
+
+#include <android/hardware/media/omx/1.0/IOmxNode.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::omx::V1_0::CodecBuffer;
+using ::android::hardware::media::omx::V1_0::IOmxBufferSource;
+using ::android::hardware::media::omx::V1_0::IOmxNode;
+using ::android::hardware::media::omx::V1_0::Message;
+using ::android::hardware::media::omx::V1_0::PortMode;
+using ::android::hardware::media::omx::V1_0::Status;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct OmxNode : public IOmxNode {
+    // Methods from ::android::hardware::media::omx::V1_0::IOmxNode follow.
+    Return<Status> freeNode() override;
+    Return<Status> sendCommand(uint32_t cmd, const hidl_vec<uint8_t>& info) override;
+    Return<void> getParameter(uint32_t index, const hidl_vec<uint8_t>& inParams, getParameter_cb _hidl_cb) override;
+    Return<Status> setParameter(uint32_t index, const hidl_vec<uint8_t>& params) override;
+    Return<void> getConfig(uint32_t index, const hidl_vec<uint8_t>& inConfig, getConfig_cb _hidl_cb) override;
+    Return<Status> setConfig(uint32_t index, const hidl_vec<uint8_t>& config) override;
+    Return<Status> setPortMode(uint32_t portIndex, PortMode mode) override;
+    Return<Status> prepareForAdaptivePlayback(uint32_t portIndex, bool enable, uint32_t maxFrameWidth, uint32_t maxFrameHeight) override;
+    Return<void> configureVideoTunnelMode(uint32_t portIndex, bool tunneled, uint32_t audioHwSync, configureVideoTunnelMode_cb _hidl_cb) override;
+    Return<void> getGraphicBufferUsage(uint32_t portIndex, getGraphicBufferUsage_cb _hidl_cb) override;
+    Return<Status> setInputSurface(const sp<IOmxBufferSource>& bufferSource) override;
+    Return<void> allocateSecureBuffer(uint32_t portIndex, uint64_t size, allocateSecureBuffer_cb _hidl_cb) override;
+    Return<void> useBuffer(uint32_t portIndex, const CodecBuffer& omxBuffer, useBuffer_cb _hidl_cb) override;
+    Return<Status> freeBuffer(uint32_t portIndex, uint32_t buffer) override;
+    Return<Status> fillBuffer(uint32_t buffer, const CodecBuffer& omxBuffer, const hidl_handle& fence) override;
+    Return<Status> emptyBuffer(uint32_t buffer, const CodecBuffer& omxBuffer, uint32_t flags, uint64_t timestampUs, const hidl_handle& fence) override;
+    Return<void> getExtensionIndex(const hidl_string& parameterName, getExtensionIndex_cb _hidl_cb) override;
+    Return<Status> dispatchMessage(const Message& msg) override;
+
+};
+
+extern "C" IOmxNode* HIDL_FETCH_IOmxNode(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXNODE_H
diff --git a/media/libstagefright/omx/hal/1.0/OmxObserver.cpp b/media/libstagefright/omx/hal/1.0/OmxObserver.cpp
new file mode 100644
index 0000000..4e946cd
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxObserver.cpp
@@ -0,0 +1,26 @@
+#include "OmxObserver.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+// Methods from ::android::hardware::media::omx::V1_0::IOmxObserver follow.
+Return<void> OmxObserver::onMessages(const hidl_vec<Message>& messages) {
+    // TODO implement
+    return Void();
+}
+
+
+IOmxObserver* HIDL_FETCH_IOmxObserver(const char* /* name */) {
+    return new OmxObserver();
+}
+
+} // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/libstagefright/omx/hal/1.0/OmxObserver.h b/media/libstagefright/omx/hal/1.0/OmxObserver.h
new file mode 100644
index 0000000..630cae3
--- /dev/null
+++ b/media/libstagefright/omx/hal/1.0/OmxObserver.h
@@ -0,0 +1,39 @@
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXOBSERVER_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXOBSERVER_H
+
+#include <android/hardware/media/omx/1.0/IOmxObserver.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::omx::V1_0::IOmxObserver;
+using ::android::hardware::media::omx::V1_0::Message;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+struct OmxObserver : public IOmxObserver {
+    // Methods from ::android::hardware::media::omx::V1_0::IOmxObserver follow.
+    Return<void> onMessages(const hidl_vec<Message>& messages) override;
+
+};
+
+extern "C" IOmxObserver* HIDL_FETCH_IOmxObserver(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_OMX_V1_0__OMXOBSERVER_H
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index f0a4ded..9cda8dc 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -911,10 +911,8 @@
 
         bool supportsPCM = (modes & 2) != 0;  // LPCM 2ch 48kHz
 
-        char val[PROPERTY_VALUE_MAX];
         if (supportsPCM
-                && property_get("media.wfd.use-pcm-audio", val, NULL)
-                && (!strcasecmp("true", val) || !strcmp("1", val))) {
+                && property_get_bool("media.wfd.use-pcm-audio", false)) {
             ALOGI("Using PCM audio.");
             mUsingPCMAudio = true;
         } else if (supportsAAC) {
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index ffbfcbb..a4cb66d 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -17,7 +17,6 @@
 LOCAL_SHARED_LIBRARIES := \
 	libresourcemanagerservice \
 	liblog \
-	libcutils \
 	libmediaplayerservice \
 	libutils \
 	libbinder \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 4d2049e..0e70ad9 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1320,7 +1320,7 @@
     ALOGV("%d died, releasing its sessions", pid);
     size_t num = mAudioSessionRefs.size();
     bool removed = false;
-    for (size_t i = 0; i< num; ) {
+    for (size_t i = 0; i < num; ) {
         AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
         ALOGV(" pid %d @ %zu", ref->mPid, i);
         if (ref->mPid == pid) {
@@ -2087,9 +2087,10 @@
     sp<StreamInHalInterface> inStream;
     status_t status = inHwHal->openInputStream(
             *input, devices, &halconfig, flags, address.string(), source, &inStream);
-    ALOGV("openInput_l() openInputStream returned input %p, SamplingRate %d"
+    ALOGV("openInput_l() openInputStream returned input %p, devices %x, SamplingRate %d"
            ", Format %#x, Channels %x, flags %#x, status %d addr %s",
             inStream.get(),
+            devices,
             halconfig.sample_rate,
             halconfig.format,
             halconfig.channel_mask,
@@ -2315,7 +2316,7 @@
     }
 
     size_t num = mAudioSessionRefs.size();
-    for (size_t i = 0; i< num; i++) {
+    for (size_t i = 0; i < num; i++) {
         AudioSessionRef *ref = mAudioSessionRefs.editItemAt(i);
         if (ref->mSessionid == audioSession && ref->mPid == caller) {
             ref->mCnt++;
@@ -2336,7 +2337,7 @@
         caller = pid;
     }
     size_t num = mAudioSessionRefs.size();
-    for (size_t i = 0; i< num; i++) {
+    for (size_t i = 0; i < num; i++) {
         AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
         if (ref->mSessionid == audioSession && ref->mPid == caller) {
             ref->mCnt--;
@@ -2354,6 +2355,18 @@
     ALOGW_IF(caller != getpid_cached, "session id %d not found for pid %d", audioSession, caller);
 }
 
+bool AudioFlinger::isSessionAcquired_l(audio_session_t audioSession)
+{
+    size_t num = mAudioSessionRefs.size();
+    for (size_t i = 0; i < num; i++) {
+        AudioSessionRef *ref = mAudioSessionRefs.itemAt(i);
+        if (ref->mSessionid == audioSession) {
+            return true;
+        }
+    }
+    return false;
+}
+
 void AudioFlinger::purgeStaleEffects_l() {
 
     ALOGV("purging stale effects");
@@ -2767,8 +2780,9 @@
         sp<Client> client = registerPid(pid);
 
         // create effect on selected output thread
+        bool pinned = (sessionId > AUDIO_SESSION_OUTPUT_MIX) && isSessionAcquired_l(sessionId);
         handle = thread->createEffect_l(client, effectClient, priority, sessionId,
-                &desc, enabled, &lStatus);
+                &desc, enabled, &lStatus, pinned);
         if (handle != 0 && id != NULL) {
             *id = handle->id();
         }
@@ -2965,7 +2979,7 @@
     ALOGV("updateOrphanEffectChains session %d index %zd", session, index);
     if (index >= 0) {
         sp<EffectChain> chain = mOrphanEffectChains.valueAt(index);
-        if (chain->removeEffect_l(effect) == 0) {
+        if (chain->removeEffect_l(effect, true) == 0) {
             ALOGV("updateOrphanEffectChains removing effect chain at index %zd", index);
             mOrphanEffectChains.removeItemsAt(index);
         }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index e9c0f93..c3bf1f9 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -19,6 +19,7 @@
 #define ANDROID_AUDIO_FLINGER_H
 
 #include "Configuration.h"
+#include <deque>
 #include <stdint.h>
 #include <sys/types.h>
 #include <limits.h>
@@ -589,6 +590,7 @@
                 void        removeNotificationClient(pid_t pid);
                 bool isNonOffloadableGlobalEffectEnabled_l();
                 void onNonOffloadableGlobalEffectEnable();
+                bool isSessionAcquired_l(audio_session_t audioSession);
 
                 // Store an effect chain to mOrphanEffectChains keyed vector.
                 // Called when a thread exits and effects are still attached to it.
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 022bf59..ad4e97b 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -60,8 +60,9 @@
                                         const wp<AudioFlinger::EffectChain>& chain,
                                         effect_descriptor_t *desc,
                                         int id,
-                                        audio_session_t sessionId)
-    : mPinned(sessionId > AUDIO_SESSION_OUTPUT_MIX),
+                                        audio_session_t sessionId,
+                                        bool pinned)
+    : mPinned(pinned),
       mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
       mDescriptor(*desc),
       // mConfig is set by configure() and not used before then
@@ -71,7 +72,7 @@
       mSuspended(false),
       mAudioFlinger(thread->mAudioFlinger)
 {
-    ALOGV("Constructor %p", this);
+    ALOGV("Constructor %p pinned %d", this, pinned);
     int lStatus;
 
     // create effect engine from effect factory
@@ -94,7 +95,9 @@
         goto Error;
     }
 
+    setOffloaded(thread->type() == ThreadBase::OFFLOAD, thread->id());
     ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface.get());
+
     return;
 Error:
     mEffectInterface.clear();
@@ -105,10 +108,10 @@
 {
     ALOGV("Destructor %p", this);
     if (mEffectInterface != 0) {
-        remove_effect_from_hal_l();
-        // release effect engine
-        mEffectInterface.clear();
+        ALOGW("EffectModule %p destructor called with unreleased interface", this);
+        release_l();
     }
+
 }
 
 status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle)
@@ -122,7 +125,7 @@
     size_t i;
     for (i = 0; i < size; i++) {
         EffectHandle *h = mHandles[i];
-        if (h == NULL || h->destroyed_l()) {
+        if (h == NULL || h->disconnected()) {
             continue;
         }
         // first non destroyed handle is considered in control
@@ -150,9 +153,14 @@
     return status;
 }
 
-size_t AudioFlinger::EffectModule::removeHandle(EffectHandle *handle)
+ssize_t AudioFlinger::EffectModule::removeHandle(EffectHandle *handle)
 {
     Mutex::Autolock _l(mLock);
+    return removeHandle_l(handle);
+}
+
+ssize_t AudioFlinger::EffectModule::removeHandle_l(EffectHandle *handle)
+{
     size_t size = mHandles.size();
     size_t i;
     for (i = 0; i < size; i++) {
@@ -161,9 +169,10 @@
         }
     }
     if (i == size) {
-        return size;
+        ALOGW("%s %p handle not found %p", __FUNCTION__, this, handle);
+        return BAD_VALUE;
     }
-    ALOGV("removeHandle() %p removed handle %p in position %zu", this, handle, i);
+    ALOGV("removeHandle_l() %p removed handle %p in position %zu", this, handle, i);
 
     mHandles.removeAt(i);
     // if removed from first place, move effect control from this handle to next in line
@@ -190,7 +199,7 @@
     // the first valid handle in the list has control over the module
     for (size_t i = 0; i < mHandles.size(); i++) {
         EffectHandle *h = mHandles[i];
-        if (h != NULL && !h->destroyed_l()) {
+        if (h != NULL && !h->disconnected()) {
             return h;
         }
     }
@@ -198,31 +207,6 @@
     return NULL;
 }
 
-size_t AudioFlinger::EffectModule::disconnect(EffectHandle *handle, bool unpinIfLast)
-{
-    ALOGV("disconnect() %p handle %p", this, handle);
-    // keep a strong reference on this EffectModule to avoid calling the
-    // destructor before we exit
-    sp<EffectModule> keep(this);
-    {
-        if (removeHandle(handle) == 0) {
-            if (!isPinned() || unpinIfLast) {
-                sp<ThreadBase> thread = mThread.promote();
-                if (thread != 0) {
-                    Mutex::Autolock _l(thread->mLock);
-                    thread->removeEffect_l(this);
-                }
-                sp<AudioFlinger> af = mAudioFlinger.promote();
-                if (af != 0) {
-                    af->updateOrphanEffectChains(this);
-                }
-                AudioSystem::unregisterEffect(mId);
-            }
-        }
-    }
-    return mHandles.size();
-}
-
 bool AudioFlinger::EffectModule::updateState() {
     Mutex::Autolock _l(mLock);
 
@@ -560,6 +544,16 @@
     return status;
 }
 
+// must be called with EffectChain::mLock held
+void AudioFlinger::EffectModule::release_l()
+{
+    if (mEffectInterface != 0) {
+        remove_effect_from_hal_l();
+        // release effect engine
+        mEffectInterface.clear();
+    }
+}
+
 status_t AudioFlinger::EffectModule::remove_effect_from_hal_l()
 {
     if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
@@ -653,7 +647,7 @@
         uint32_t size = (replySize == NULL) ? 0 : *replySize;
         for (size_t i = 1; i < mHandles.size(); i++) {
             EffectHandle *h = mHandles[i];
-            if (h != NULL && !h->destroyed_l()) {
+            if (h != NULL && !h->disconnected()) {
                 h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData);
             }
         }
@@ -706,7 +700,7 @@
         }
         for (size_t i = 1; i < mHandles.size(); i++) {
             EffectHandle *h = mHandles[i];
-            if (h != NULL && !h->destroyed_l()) {
+            if (h != NULL && !h->disconnected()) {
                 h->setEnabled(enabled);
             }
         }
@@ -866,8 +860,7 @@
     Mutex::Autolock _l(mLock);
     for (size_t i = 0; i < mHandles.size(); i++) {
         EffectHandle *handle = mHandles[i];
-        if (handle != NULL && !handle->destroyed_l()) {
-            handle->effect().clear();
+        if (handle != NULL && !handle->disconnected()) {
             if (handle->hasControl()) {
                 enabled = handle->enabled();
             }
@@ -1093,7 +1086,7 @@
     result.append("\t\t\t  Pid Priority Ctrl Locked client server\n");
     for (size_t i = 0; i < mHandles.size(); ++i) {
         EffectHandle *handle = mHandles[i];
-        if (handle != NULL && !handle->destroyed_l()) {
+        if (handle != NULL && !handle->disconnected()) {
             handle->dumpToBuffer(buffer, SIZE);
             result.append(buffer);
         }
@@ -1119,7 +1112,7 @@
                                         int32_t priority)
     : BnEffect(),
     mEffect(effect), mEffectClient(effectClient), mClient(client), mCblk(NULL),
-    mPriority(priority), mHasControl(false), mEnabled(false), mDestroyed(false)
+    mPriority(priority), mHasControl(false), mEnabled(false), mDisconnected(false)
 {
     ALOGV("constructor %p", this);
 
@@ -1142,14 +1135,6 @@
 AudioFlinger::EffectHandle::~EffectHandle()
 {
     ALOGV("Destructor %p", this);
-
-    if (mEffect == 0) {
-        mDestroyed = true;
-        return;
-    }
-    mEffect->lock();
-    mDestroyed = true;
-    mEffect->unlock();
     disconnect(false);
 }
 
@@ -1160,13 +1145,15 @@
 
 status_t AudioFlinger::EffectHandle::enable()
 {
+    AutoMutex _l(mLock);
     ALOGV("enable %p", this);
+    sp<EffectModule> effect = mEffect.promote();
+    if (effect == 0 || mDisconnected) {
+        return DEAD_OBJECT;
+    }
     if (!mHasControl) {
         return INVALID_OPERATION;
     }
-    if (mEffect == 0) {
-        return DEAD_OBJECT;
-    }
 
     if (mEnabled) {
         return NO_ERROR;
@@ -1174,20 +1161,20 @@
 
     mEnabled = true;
 
-    sp<ThreadBase> thread = mEffect->thread().promote();
+    sp<ThreadBase> thread = effect->thread().promote();
     if (thread != 0) {
-        thread->checkSuspendOnEffectEnabled(mEffect, true, mEffect->sessionId());
+        thread->checkSuspendOnEffectEnabled(effect, true, effect->sessionId());
     }
 
     // checkSuspendOnEffectEnabled() can suspend this same effect when enabled
-    if (mEffect->suspended()) {
+    if (effect->suspended()) {
         return NO_ERROR;
     }
 
-    status_t status = mEffect->setEnabled(true);
+    status_t status = effect->setEnabled(true);
     if (status != NO_ERROR) {
         if (thread != 0) {
-            thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+            thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
         }
         mEnabled = false;
     } else {
@@ -1197,12 +1184,12 @@
                 Mutex::Autolock _l(t->mLock);
                 t->broadcast_l();
             }
-            if (!mEffect->isOffloadable()) {
+            if (!effect->isOffloadable()) {
                 if (thread->type() == ThreadBase::OFFLOAD) {
                     PlaybackThread *t = (PlaybackThread *)thread.get();
                     t->invalidateTracks(AUDIO_STREAM_MUSIC);
                 }
-                if (mEffect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+                if (effect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
                     thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable();
                 }
             }
@@ -1214,27 +1201,29 @@
 status_t AudioFlinger::EffectHandle::disable()
 {
     ALOGV("disable %p", this);
+    AutoMutex _l(mLock);
+    sp<EffectModule> effect = mEffect.promote();
+    if (effect == 0 || mDisconnected) {
+        return DEAD_OBJECT;
+    }
     if (!mHasControl) {
         return INVALID_OPERATION;
     }
-    if (mEffect == 0) {
-        return DEAD_OBJECT;
-    }
 
     if (!mEnabled) {
         return NO_ERROR;
     }
     mEnabled = false;
 
-    if (mEffect->suspended()) {
+    if (effect->suspended()) {
         return NO_ERROR;
     }
 
-    status_t status = mEffect->setEnabled(false);
+    status_t status = effect->setEnabled(false);
 
-    sp<ThreadBase> thread = mEffect->thread().promote();
+    sp<ThreadBase> thread = effect->thread().promote();
     if (thread != 0) {
-        thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+        thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
         if (thread->type() == ThreadBase::OFFLOAD) {
             PlaybackThread *t = (PlaybackThread *)thread.get();
             Mutex::Autolock _l(t->mLock);
@@ -1247,25 +1236,32 @@
 
 void AudioFlinger::EffectHandle::disconnect()
 {
+    ALOGV("%s %p", __FUNCTION__, this);
     disconnect(true);
 }
 
 void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast)
 {
-    ALOGV("disconnect(%s)", unpinIfLast ? "true" : "false");
-    if (mEffect == 0) {
+    AutoMutex _l(mLock);
+    ALOGV("disconnect(%s) %p", unpinIfLast ? "true" : "false", this);
+    if (mDisconnected) {
+        if (unpinIfLast) {
+            android_errorWriteLog(0x534e4554, "32707507");
+        }
         return;
     }
-    // restore suspended effects if the disconnected handle was enabled and the last one.
-    if ((mEffect->disconnect(this, unpinIfLast) == 0) && mEnabled) {
-        sp<ThreadBase> thread = mEffect->thread().promote();
-        if (thread != 0) {
-            thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+    mDisconnected = true;
+    sp<ThreadBase> thread;
+    {
+        sp<EffectModule> effect = mEffect.promote();
+        if (effect != 0) {
+            thread = effect->thread().promote();
         }
     }
+    if (thread != 0) {
+        thread->disconnectEffectHandle(this, unpinIfLast);
+    }
 
-    // release sp on module => module destructor can be called now
-    mEffect.clear();
     if (mClient != 0) {
         if (mCblk != NULL) {
             // unlike ~TrackBase(), mCblk is never a local new, so don't delete
@@ -1285,15 +1281,17 @@
                                              void *pReplyData)
 {
     ALOGVV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p",
-            cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
+            cmdCode, mHasControl, mEffect.unsafe_get());
 
+    AutoMutex _l(mLock);
+    sp<EffectModule> effect = mEffect.promote();
+    if (effect == 0 || mDisconnected) {
+        return DEAD_OBJECT;
+    }
     // only get parameter command is permitted for applications not controlling the effect
     if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
         return INVALID_OPERATION;
     }
-    if (mEffect == 0) {
-        return DEAD_OBJECT;
-    }
     if (mClient == 0) {
         return INVALID_OPERATION;
     }
@@ -1338,7 +1336,7 @@
 
             int reply = 0;
             uint32_t rsize = sizeof(reply);
-            status_t ret = mEffect->command(EFFECT_CMD_SET_PARAM,
+            status_t ret = effect->command(EFFECT_CMD_SET_PARAM,
                                             size,
                                             param,
                                             &rsize,
@@ -1375,7 +1373,7 @@
         return disable();
     }
 
-    return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+    return effect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
 }
 
 void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal, bool enabled)
@@ -1457,7 +1455,6 @@
     if (mOwnInBuffer) {
         delete mInBuffer;
     }
-
 }
 
 // getEffectFromDesc_l() must be called with ThreadBase::mLock held
@@ -1573,13 +1570,38 @@
     }
 }
 
-// addEffect_l() must be called with PlaybackThread::mLock held
+// createEffect_l() must be called with ThreadBase::mLock held
+status_t AudioFlinger::EffectChain::createEffect_l(sp<EffectModule>& effect,
+                                                   ThreadBase *thread,
+                                                   effect_descriptor_t *desc,
+                                                   int id,
+                                                   audio_session_t sessionId,
+                                                   bool pinned)
+{
+    Mutex::Autolock _l(mLock);
+    effect = new EffectModule(thread, this, desc, id, sessionId, pinned);
+    status_t lStatus = effect->status();
+    if (lStatus == NO_ERROR) {
+        lStatus = addEffect_ll(effect);
+    }
+    if (lStatus != NO_ERROR) {
+        effect.clear();
+    }
+    return lStatus;
+}
+
+// addEffect_l() must be called with ThreadBase::mLock held
 status_t AudioFlinger::EffectChain::addEffect_l(const sp<EffectModule>& effect)
 {
+    Mutex::Autolock _l(mLock);
+    return addEffect_ll(effect);
+}
+// addEffect_l() must be called with ThreadBase::mLock and EffectChain::mLock held
+status_t AudioFlinger::EffectChain::addEffect_ll(const sp<EffectModule>& effect)
+{
     effect_descriptor_t desc = effect->desc();
     uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
 
-    Mutex::Autolock _l(mLock);
     effect->setChain(this);
     sp<ThreadBase> thread = mThread.promote();
     if (thread == 0) {
@@ -1689,8 +1711,9 @@
     return NO_ERROR;
 }
 
-// removeEffect_l() must be called with PlaybackThread::mLock held
-size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect)
+// removeEffect_l() must be called with ThreadBase::mLock held
+size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect,
+                                                 bool release)
 {
     Mutex::Autolock _l(mLock);
     size_t size = mEffects.size();
@@ -1705,6 +1728,10 @@
                     mEffects[i]->state() == EffectModule::STOPPING) {
                 mEffects[i]->stop();
             }
+            if (release) {
+                mEffects[i]->release_l();
+            }
+
             if (type == EFFECT_FLAG_TYPE_AUXILIARY) {
                 delete[] effect->inBuffer();
             } else {
@@ -1716,6 +1743,7 @@
             mEffects.removeAt(i);
             ALOGV("removeEffect_l() effect %p, removed from chain %p at rank %zu", effect.get(),
                     this, i);
+
             break;
         }
     }
@@ -1723,7 +1751,7 @@
     return mEffects.size();
 }
 
-// setDevice_l() must be called with PlaybackThread::mLock held
+// setDevice_l() must be called with ThreadBase::mLock held
 void AudioFlinger::EffectChain::setDevice_l(audio_devices_t device)
 {
     size_t size = mEffects.size();
@@ -1732,7 +1760,7 @@
     }
 }
 
-// setMode_l() must be called with PlaybackThread::mLock held
+// setMode_l() must be called with ThreadBase::mLock held
 void AudioFlinger::EffectChain::setMode_l(audio_mode_t mode)
 {
     size_t size = mEffects.size();
@@ -1741,7 +1769,7 @@
     }
 }
 
-// setAudioSource_l() must be called with PlaybackThread::mLock held
+// setAudioSource_l() must be called with ThreadBase::mLock held
 void AudioFlinger::EffectChain::setAudioSource_l(audio_source_t source)
 {
     size_t size = mEffects.size();
@@ -1750,7 +1778,7 @@
     }
 }
 
-// setVolume_l() must be called with PlaybackThread::mLock or EffectChain::mLock held
+// setVolume_l() must be called with ThreadBase::mLock or EffectChain::mLock held
 bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right, bool force)
 {
     uint32_t newLeft = *left;
@@ -1811,7 +1839,7 @@
     return hasControl;
 }
 
-// resetVolume_l() must be called with PlaybackThread::mLock or EffectChain::mLock held
+// resetVolume_l() must be called with ThreadBase::mLock or EffectChain::mLock held
 void AudioFlinger::EffectChain::resetVolume_l()
 {
     if ((mLeftVolume != UINT_MAX) && (mRightVolume != UINT_MAX)) {
@@ -1912,7 +1940,7 @@
                     effect->setSuspended(false);
                     effect->lock();
                     EffectHandle *handle = effect->controlHandle_l();
-                    if (handle != NULL && !handle->destroyed_l()) {
+                    if (handle != NULL && !handle->disconnected()) {
                         effect->setEnabled_l(handle->enabled());
                     }
                     effect->unlock();
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index a19b7fd..d745121 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -25,10 +25,11 @@
 // state changes or resource modifications. Always respect the following order
 // if multiple mutexes must be acquired to avoid cross deadlock:
 // AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
+// AudioHandle -> ThreadBase -> EffectChain -> EffectModule
 // In addition, methods that lock the AudioPolicyService mutex (getOutputForEffect(),
-// startOutput()...) should never be called with AudioFlinger or Threadbase mutex locked
-// to avoid cross deadlock with other clients calling AudioPolicyService methods that in turn
-// call AudioFlinger thus locking the same mutexes in the reverse order.
+// startOutput(), getInputForAttr(), releaseInput()...) should never be called with AudioFlinger or
+// Threadbase mutex locked to avoid cross deadlock with other clients calling AudioPolicyService
+// methods that in turn call AudioFlinger thus locking the same mutexes in the reverse order.
 
 // The EffectModule class is a wrapper object controlling the effect engine implementation
 // in the effect library. It prevents concurrent calls to process() and command() functions
@@ -45,7 +46,8 @@
                     const wp<AudioFlinger::EffectChain>& chain,
                     effect_descriptor_t *desc,
                     int id,
-                    audio_session_t sessionId);
+                    audio_session_t sessionId,
+                    bool pinned);
     virtual ~EffectModule();
 
     enum effect_state {
@@ -93,8 +95,8 @@
     const wp<ThreadBase>& thread() { return mThread; }
 
     status_t addHandle(EffectHandle *handle);
-    size_t disconnect(EffectHandle *handle, bool unpinIfLast);
-    size_t removeHandle(EffectHandle *handle);
+    ssize_t removeHandle(EffectHandle *handle);
+    ssize_t removeHandle_l(EffectHandle *handle);
 
     const effect_descriptor_t& desc() const { return mDescriptor; }
     wp<EffectChain>&     chain() { return mChain; }
@@ -124,6 +126,7 @@
     status_t         setOffloaded(bool offloaded, audio_io_handle_t io);
     bool             isOffloaded() const;
     void             addEffectToHal_l();
+    void             release_l();
 
     void             dump(int fd, const Vector<String16>& args);
 
@@ -208,12 +211,17 @@
     bool enabled() const { return mEnabled; }
 
     // Getters
-    int id() const { return mEffect->id(); }
+    wp<EffectModule> effect() const { return mEffect; }
+    int id() const {
+        sp<EffectModule> effect = mEffect.promote();
+        if (effect == 0) {
+            return 0;
+        }
+        return effect->id();
+    }
     int priority() const { return mPriority; }
     bool hasControl() const { return mHasControl; }
-    sp<EffectModule> effect() const { return mEffect; }
-    // destroyed_l() must be called with the associated EffectModule mLock held
-    bool destroyed_l() const { return mDestroyed; }
+    bool disconnected() const { return mDisconnected; }
 
     void dumpToBuffer(char* buffer, size_t size);
 
@@ -222,7 +230,8 @@
     EffectHandle(const EffectHandle&);
     EffectHandle& operator =(const EffectHandle&);
 
-    sp<EffectModule> mEffect;           // pointer to controlled EffectModule
+    Mutex mLock;                        // protects IEffect method calls
+    wp<EffectModule> mEffect;           // pointer to controlled EffectModule
     sp<IEffectClient> mEffectClient;    // callback interface for client notifications
     /*const*/ sp<Client> mClient;       // client for shared memory allocation, see disconnect()
     sp<IMemory>         mCblkMemory;    // shared memory for control block
@@ -233,8 +242,7 @@
     bool mHasControl;                   // true if this handle is controlling the effect
     bool mEnabled;                      // cached enable state: needed when the effect is
                                         // restored after being suspended
-    bool mDestroyed;                    // Set to true by destructor. Access with EffectModule
-                                        // mLock held
+    bool mDisconnected;                 // Set to true by disconnect()
 };
 
 // the EffectChain class represents a group of effects associated to one audio session.
@@ -269,8 +277,15 @@
         mLock.unlock();
     }
 
+    status_t createEffect_l(sp<EffectModule>& effect,
+                            ThreadBase *thread,
+                            effect_descriptor_t *desc,
+                            int id,
+                            audio_session_t sessionId,
+                            bool pinned);
     status_t addEffect_l(const sp<EffectModule>& handle);
-    size_t removeEffect_l(const sp<EffectModule>& handle);
+    status_t addEffect_ll(const sp<EffectModule>& handle);
+    size_t removeEffect_l(const sp<EffectModule>& handle, bool release = false);
 
     audio_session_t sessionId() const { return mSessionId; }
     void setSessionId(audio_session_t sessionId) { mSessionId = sessionId; }
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 1d7b946..9966eeb 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1267,7 +1267,8 @@
         audio_session_t sessionId,
         effect_descriptor_t *desc,
         int *enabled,
-        status_t *status)
+        status_t *status,
+        bool pinned)
 {
     sp<EffectModule> effect;
     sp<EffectHandle> handle;
@@ -1317,14 +1318,7 @@
             }
             effectRegistered = true;
             // create a new effect module if none present in the chain
-            effect = new EffectModule(this, chain, desc, id, sessionId);
-            lStatus = effect->status();
-            if (lStatus != NO_ERROR) {
-                goto Exit;
-            }
-            effect->setOffloaded(mType == OFFLOAD, mId);
-
-            lStatus = chain->addEffect_l(effect);
+            lStatus = chain->createEffect_l(effect, this, desc, id, sessionId, pinned);
             if (lStatus != NO_ERROR) {
                 goto Exit;
             }
@@ -1365,6 +1359,33 @@
     return handle;
 }
 
+void AudioFlinger::ThreadBase::disconnectEffectHandle(EffectHandle *handle,
+                                                      bool unpinIfLast)
+{
+    bool remove = false;
+    sp<EffectModule> effect;
+    {
+        Mutex::Autolock _l(mLock);
+
+        effect = handle->effect().promote();
+        if (effect == 0) {
+            return;
+        }
+        // restore suspended effects if the disconnected handle was enabled and the last one.
+        remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast);
+        if (remove) {
+            removeEffect_l(effect, true);
+        }
+    }
+    if (remove) {
+        mAudioFlinger->updateOrphanEffectChains(effect);
+        AudioSystem::unregisterEffect(effect->id());
+        if (handle->enabled()) {
+            checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
+        }
+    }
+}
+
 sp<AudioFlinger::EffectModule> AudioFlinger::ThreadBase::getEffect(audio_session_t sessionId,
         int effectId)
 {
@@ -1425,9 +1446,9 @@
     return NO_ERROR;
 }
 
-void AudioFlinger::ThreadBase::removeEffect_l(const sp<EffectModule>& effect) {
+void AudioFlinger::ThreadBase::removeEffect_l(const sp<EffectModule>& effect, bool release) {
 
-    ALOGV("removeEffect_l() %p effect %p", this, effect.get());
+    ALOGV("%s %p effect %p", __FUNCTION__, this, effect.get());
     effect_descriptor_t desc = effect->desc();
     if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
         detachAuxEffect_l(effect->id());
@@ -1436,7 +1457,7 @@
     sp<EffectChain> chain = effect->chain().promote();
     if (chain != 0) {
         // remove effect chain if removing last effect
-        if (chain->removeEffect_l(effect) == 0) {
+        if (chain->removeEffect_l(effect, release) == 0) {
             removeEffectChain_l(chain);
         }
     } else {
@@ -1602,6 +1623,7 @@
     dumpInternals(fd, args);
     dumpTracks(fd, args);
     dumpEffectChains(fd, args);
+    mLocalLog.dump(fd, args, "  " /* prefix */);
 }
 
 void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args __unused)
@@ -2072,6 +2094,10 @@
             chain->incActiveTrackCnt();
         }
 
+        char buffer[256];
+        track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+        mLocalLog.log("addTrack_l    (%p) %s", track.get(), buffer + 4); // log for analysis
+
         status = NO_ERROR;
     }
 
@@ -2097,6 +2123,11 @@
 void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
 {
     track->triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+
+    char buffer[256];
+    track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+    mLocalLog.log("removeTrack_l (%p) %s", track.get(), buffer + 4); // log for analysis
+
     mTracks.remove(track);
     deleteTrackName_l(track->name());
     // redundant as track is about to be destroyed, for dumpsys only
@@ -3264,6 +3295,10 @@
             }
             if (track->isTerminated()) {
                 removeTrack_l(track);
+            } else { // inactive but not terminated
+                char buffer[256];
+                track->dump(buffer, ARRAY_SIZE(buffer), false /* active */);
+                mLocalLog.log("removeTracks_l(%p) %s", track.get(), buffer + 4);
             }
         }
     }
@@ -3710,6 +3745,15 @@
         FastMixerStateQueue *sq = mFastMixer->sq();
         FastMixerState *state = sq->begin();
         if (!(state->mCommand & FastMixerState::IDLE)) {
+            // Report any frames trapped in the Monopipe
+            MonoPipe *monoPipe = (MonoPipe *)mPipeSink.get();
+            const long long pipeFrames = monoPipe->maxFrames() - monoPipe->availableToWrite();
+            mLocalLog.log("threadLoop_standby: framesWritten:%lld  suspendedFrames:%lld  "
+                    "monoPipeWritten:%lld  monoPipeLeft:%lld",
+                    (long long)mFramesWritten, (long long)mSuspendedFrames,
+                    (long long)mPipeSink->framesWritten(), pipeFrames);
+            mLocalLog.log("threadLoop_standby: %s", mTimestamp.toString().c_str());
+
             state->mCommand = FastMixerState::COLD_IDLE;
             state->mColdFutexAddr = &mFastMixerFutex;
             state->mColdGen++;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 5235cde..d261ea5 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -295,7 +295,8 @@
                                     audio_session_t sessionId,
                                     effect_descriptor_t *desc,
                                     int *enabled,
-                                    status_t *status /*non-NULL*/);
+                                    status_t *status /*non-NULL*/,
+                                    bool pinned);
 
                 // return values for hasAudioSession (bit field)
                 enum effect_state {
@@ -334,7 +335,9 @@
                 status_t addEffect_l(const sp< EffectModule>& effect);
                 // remove and effect module. Also removes the effect chain is this was the last
                 // effect
-                void removeEffect_l(const sp< EffectModule>& effect);
+                void removeEffect_l(const sp< EffectModule>& effect, bool release = false);
+                // disconnect an effect handle from module and destroy module if last handle
+                void disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast);
                 // detach all tracks connected to an auxiliary effect
     virtual     void detachAuxEffect_l(int effectId __unused) {}
                 // returns a combination of:
@@ -861,6 +864,78 @@
     uint32_t                mScreenState;   // cached copy of gScreenState
     static const size_t     kFastMixerLogSize = 4 * 1024;
     sp<NBLog::Writer>       mFastMixerNBLogWriter;
+
+    // Do not call from a sched_fifo thread as it uses a system time call
+    // and obtains a local mutex.
+    class LocalLog {
+    public:
+        void log(const char *fmt, ...) {
+            va_list val;
+            va_start(val, fmt);
+
+            // format to buffer
+            char buffer[512];
+            int length = vsnprintf(buffer, sizeof(buffer), fmt, val);
+            if (length >= (signed)sizeof(buffer)) {
+                length = sizeof(buffer) - 1;
+            }
+
+            // strip out trailing newline
+            while (length > 0 && buffer[length - 1] == '\n') {
+                buffer[--length] = 0;
+            }
+
+            // store in circular array
+            AutoMutex _l(mLock);
+            mLog.emplace_back(
+                    std::make_pair(systemTime(SYSTEM_TIME_REALTIME), std::string(buffer)));
+            if (mLog.size() > kLogSize) {
+                mLog.pop_front();
+            }
+
+            va_end(val);
+        }
+
+        void dump(int fd, const Vector<String16>& args, const char *prefix = "") {
+            if (!AudioFlinger::dumpTryLock(mLock)) return; // a local lock, shouldn't happen
+            if (mLog.size() > 0) {
+                bool dumpAll = false;
+                for (const auto &arg : args) {
+                    if (arg == String16("--locallog")) {
+                        dumpAll = true;
+                    }
+                }
+
+                dprintf(fd, "Local Log:\n");
+                auto it = mLog.begin();
+                if (!dumpAll && mLog.size() > kLogPrint) {
+                    it += (mLog.size() - kLogPrint);
+                }
+                for (; it != mLog.end(); ++it) {
+                    const int64_t ns = it->first;
+                    const int ns_per_sec = 1000000000;
+                    const time_t sec = ns / ns_per_sec;
+                    struct tm tm;
+                    localtime_r(&sec, &tm);
+
+                    dprintf(fd, "%s%02d-%02d %02d:%02d:%02d.%03d %s\n",
+                            prefix,
+                            tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
+                            tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+                            (int)(ns % ns_per_sec / 1000000),
+                            it->second.c_str());
+                }
+            }
+            mLock.unlock();
+        }
+
+    private:
+        Mutex mLock;
+        static const size_t kLogSize = 256; // full history
+        static const size_t kLogPrint = 32; // default print history
+        std::deque<std::pair<int64_t, std::string>> mLog;
+    } mLocalLog;
+
 public:
     virtual     bool        hasFastMixer() const = 0;
     virtual     FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex __unused) const
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 8f134c1..e8e27e4 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -459,7 +459,7 @@
 /*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
 {
     result.append("    Name Active Client Type      Fmt Chn mask Session fCount S F SRate  "
-                  "L dB  R dB    Server Main buf  Aux Buf Flags UndFrmCnt\n");
+                  "L dB  R dB    Server Main buf  Aux buf Flags UndFrmCnt  Flushed\n");
 }
 
 void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size, bool active)
@@ -526,7 +526,7 @@
         break;
     }
     snprintf(&buffer[8], size-8, " %6s %6u %4u %08X %08X %7u %6zu %1c %1d %5u %5.2g %5.2g  "
-                                 "%08X %p %p 0x%03X %9u%c\n",
+                                 "%08X %08zX %08zX 0x%03X %9u%c %7u\n",
             active ? "yes" : "no",
             (mClient == 0) ? getpid_cached : mClient->pid(),
             mStreamType,
@@ -540,11 +540,12 @@
             20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))),
             20.0 * log10(float_from_gain(gain_minifloat_unpack_right(vlr))),
             mCblk->mServer,
-            mMainBuffer,
-            mAuxBuffer,
+            (size_t)mMainBuffer, // use %zX as %p appends 0x
+            (size_t)mAuxBuffer,  // use %zX as %p appends 0x
             mCblk->mFlags,
             mAudioTrackServerProxy->getUnderrunFrames(),
-            nowInUnderrun);
+            nowInUnderrun,
+            (unsigned)mAudioTrackServerProxy->framesFlushed() % 10000000); // 7 digits
 }
 
 uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const {
diff --git a/services/audiopolicy/config/a2dp_audio_policy_configuration.xml b/services/audiopolicy/config/a2dp_audio_policy_configuration.xml
index ced7463..7bcab5c 100644
--- a/services/audiopolicy/config/a2dp_audio_policy_configuration.xml
+++ b/services/audiopolicy/config/a2dp_audio_policy_configuration.xml
@@ -2,11 +2,7 @@
 <!-- A2dp Audio HAL Audio Policy Configuration file -->
 <module name="a2dp" halVersion="2.0">
     <mixPorts>
-        <mixPort name="a2dp output" role="source">
-            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
-                     samplingRates="44100"
-                     channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
-        </mixPort>
+        <mixPort name="a2dp output" role="source"/>
         <mixPort name="a2dp input" role="sink">
             <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                      samplingRates="44100,48000"
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 9a7839b..32b99ca 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -911,9 +911,7 @@
             CameraParameters::FALSE);
     }
 
-    char value[PROPERTY_VALUE_MAX];
-    property_get("camera.disable_zsl_mode", value, "0");
-    if (!strcmp(value,"1") || slowJpegMode) {
+    if (slowJpegMode || property_get_bool("camera.disable_zsl_mode", false)) {
         ALOGI("Camera %d: Disabling ZSL mode", cameraId);
         allowZslMode = false;
     } else {
diff --git a/services/mediaanalytics/Android.mk b/services/mediaanalytics/Android.mk
new file mode 100644
index 0000000..76a5c1c
--- /dev/null
+++ b/services/mediaanalytics/Android.mk
@@ -0,0 +1,32 @@
+# Media Statistics service
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	main_mediaanalytics.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	liblog \
+	libmedia \
+	libmediaanalyticsservice \
+	libutils \
+	libbinder \
+	libicuuc
+
+LOCAL_STATIC_LIBRARIES := \
+        libicuandroid_utils \
+        libregistermsext
+
+LOCAL_C_INCLUDES := \
+    frameworks/av/media/libmediaanalyticsservice
+
+LOCAL_MODULE:= mediaanalytics
+
+LOCAL_INIT_RC := mediaanalytics.rc
+
+LOCAL_CFLAGS := -Werror -Wall
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/mediaanalytics/main_mediaanalytics.cpp b/services/mediaanalytics/main_mediaanalytics.cpp
new file mode 100644
index 0000000..ba601ee
--- /dev/null
+++ b/services/mediaanalytics/main_mediaanalytics.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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 "mediaanalytics"
+//#define LOG_NDEBUG 0
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+//#include "RegisterExtensions.h"
+
+// from LOCAL_C_INCLUDES
+#include "IcuUtils.h"
+#include "MediaAnalyticsService.h"
+
+using namespace android;
+
+int main(int argc __unused, char **argv __unused)
+{
+    signal(SIGPIPE, SIG_IGN);
+
+    sp<ProcessState> proc(ProcessState::self());
+    sp<IServiceManager> sm(defaultServiceManager());
+    ALOGI("ServiceManager: %p", sm.get());
+
+    InitializeIcuOrDie();
+    MediaAnalyticsService::instantiate();
+    ProcessState::self()->startThreadPool();
+    IPCThreadState::self()->joinThreadPool();
+}
diff --git a/services/mediaanalytics/mediaanalytics.rc b/services/mediaanalytics/mediaanalytics.rc
new file mode 100644
index 0000000..0af69f5
--- /dev/null
+++ b/services/mediaanalytics/mediaanalytics.rc
@@ -0,0 +1,5 @@
+service mediaanalytics /system/bin/mediaanalytics
+    class main
+    user media
+    ioprio rt 4
+    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index 38aa472..73109e1 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -23,7 +23,6 @@
 
 LOCAL_SHARED_LIBRARIES:= \
     libbinder \
-    libcutils \
     liblog \
     libmediadrm \
     libutils \
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 3ba7f62..54f9b95 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -73,8 +73,7 @@
     ALOGI("loaded default module %s, handle %d", descriptor.properties.description,
                                                  descriptor.handle);
 
-    sp<ISoundTriggerClient> client;
-    sp<Module> module = new Module(this, halInterface, descriptor, client);
+    sp<Module> module = new Module(this, halInterface, descriptor);
     mModules.add(descriptor.handle, module);
     mCallbackThread = new CallbackThread(this);
 }
@@ -126,11 +125,13 @@
     }
     sp<Module> module = mModules.valueAt(index);
 
-    module->setClient(client);
-    IInterface::asBinder(client)->linkToDeath(module);
-    moduleInterface = module;
+    sp<ModuleClient> moduleClient = module->addClient(client);
+    if (moduleClient == 0) {
+        return NO_INIT;
+    }
 
-    module->setCaptureState_l(mCaptureState);
+    moduleClient->setCaptureState_l(mCaptureState);
+    moduleInterface = moduleClient;
 
     return NO_ERROR;
 }
@@ -147,14 +148,6 @@
 }
 
 
-void SoundTriggerHwService::detachModule(const sp<Module>& module)
-{
-    ALOGV("detachModule");
-    AutoMutex lock(mServiceLock);
-    module->clearClient();
-}
-
-
 static const int kDumpLockRetries = 50;
 static const int kDumpLockSleep = 60000;
 
@@ -276,8 +269,10 @@
          return;
      }
 
-     sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
-                                                  eventMemory, strongModule));
+    sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
+                                                        eventMemory);
+    callbackEvent->setModule(strongModule);
+    sendCallbackEvent_l(callbackEvent);
 }
 
 // static
@@ -329,8 +324,10 @@
     if (strongModule == 0) {
         return;
     }
-    sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_SOUNDMODEL,
-                                                 eventMemory, strongModule));
+    sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SOUNDMODEL,
+                                                        eventMemory);
+    callbackEvent->setModule(strongModule);
+    sendCallbackEvent_l(callbackEvent);
 }
 
 
@@ -366,8 +363,23 @@
     if (strongModule == 0) {
         return;
     }
-    sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
-                                                 eventMemory, strongModule));
+    sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
+                                                        eventMemory);
+    callbackEvent->setModule(strongModule);
+    sendCallbackEvent_l(callbackEvent);
+}
+
+void SoundTriggerHwService::sendServiceStateEvent_l(sound_trigger_service_state_t state,
+                                                    ModuleClient *moduleClient)
+{
+    sp<IMemory> eventMemory = prepareServiceStateEvent_l(state);
+    if (eventMemory == 0) {
+        return;
+    }
+    sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
+                                                        eventMemory);
+    callbackEvent->setModuleClient(moduleClient);
+    sendCallbackEvent_l(callbackEvent);
 }
 
 // call with mServiceLock held
@@ -380,14 +392,25 @@
 {
     ALOGV("onCallbackEvent");
     sp<Module> module;
+    sp<ModuleClient> moduleClient;
     {
         AutoMutex lock(mServiceLock);
+        //CallbackEvent is either for Module or ModuleClient
         module = event->mModule.promote();
         if (module == 0) {
-            return;
+            moduleClient = event->mModuleClient.promote();
+            if (moduleClient == 0) {
+                return;
+            }
         }
     }
-    module->onCallbackEvent(event);
+    if (module != 0) {
+        ALOGV("onCallbackEvent for module");
+        module->onCallbackEvent(event);
+    } else if (moduleClient != 0) {
+        ALOGV("onCallbackEvent for moduleClient");
+        moduleClient->onCallbackEvent(event);
+    }
     {
         AutoMutex lock(mServiceLock);
         // clear now to execute with mServiceLock locked
@@ -457,9 +480,8 @@
     mCallbackCond.signal();
 }
 
-SoundTriggerHwService::CallbackEvent::CallbackEvent(event_type type, sp<IMemory> memory,
-                                                    wp<Module> module)
-    : mType(type), mMemory(memory), mModule(module)
+SoundTriggerHwService::CallbackEvent::CallbackEvent(event_type type, sp<IMemory> memory)
+    : mType(type), mMemory(memory)
 {
 }
 
@@ -473,25 +495,59 @@
 
 SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service,
                                       const sp<SoundTriggerHalInterface>& halInterface,
-                                      sound_trigger_module_descriptor descriptor,
-                                      const sp<ISoundTriggerClient>& client)
+                                      sound_trigger_module_descriptor descriptor)
  : mService(service), mHalInterface(halInterface), mDescriptor(descriptor),
-   mClient(client), mServiceState(SOUND_TRIGGER_STATE_NO_INIT)
+   mServiceState(SOUND_TRIGGER_STATE_NO_INIT)
 {
 }
 
 SoundTriggerHwService::Module::~Module() {
+    mModuleClients.clear();
 }
 
-void SoundTriggerHwService::Module::detach() {
-    ALOGV("detach()");
-    if (!captureHotwordAllowed()) {
+sp<SoundTriggerHwService::ModuleClient>
+SoundTriggerHwService::Module::addClient(const sp<ISoundTriggerClient>& client)
+{
+    AutoMutex lock(mLock);
+    sp<ModuleClient> moduleClient;
+
+    for (size_t i = 0; i < mModuleClients.size(); i++) {
+        if (mModuleClients[i]->client() == client) {
+            // Client already present, reuse client
+            return moduleClient;
+        }
+    }
+    moduleClient = new ModuleClient(this, client);
+
+    ALOGV("addClient() client %p", moduleClient.get());
+    mModuleClients.add(moduleClient);
+
+    return moduleClient;
+}
+
+void SoundTriggerHwService::Module::detach(const sp<ModuleClient>& moduleClient)
+{
+    ALOGV("Module::detach()");
+    AutoMutex lock(mLock);
+    ssize_t index = -1;
+
+    for (size_t i = 0; i < mModuleClients.size(); i++) {
+        if (mModuleClients[i] == moduleClient) {
+            index = i;
+            break;
+        }
+    }
+    if (index == -1) {
         return;
     }
-    {
-        AutoMutex lock(mLock);
-        for (size_t i = 0; i < mModels.size(); i++) {
-            sp<Model> model = mModels.valueAt(i);
+
+    ALOGV("remove client %p", moduleClient.get());
+    mModuleClients.removeAt(index);
+
+    for (size_t i = 0; i < mModels.size(); i++) {
+        sp<Model> model = mModels.valueAt(i);
+        if (moduleClient == model->mModuleClient) {
+            mModels.removeItemsAt(i);
             ALOGV("detach() unloading model %d", model->mHandle);
             if (mHalInterface != 0) {
                 if (model->mState == Model::STATE_ACTIVE) {
@@ -499,29 +555,20 @@
                 }
                 mHalInterface->unloadSoundModel(model->mHandle);
             }
+            AudioSystem::releaseSoundTriggerSession(model->mCaptureSession);
+            mHalInterface->unloadSoundModel(model->mHandle);
         }
-        mModels.clear();
     }
-    if (mClient != 0) {
-        IInterface::asBinder(mClient)->unlinkToDeath(this);
-    }
-    sp<SoundTriggerHwService> service = mService.promote();
-    if (service == 0) {
-        return;
-    }
-    service->detachModule(this);
 }
 
 status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& modelMemory,
-                                sound_model_handle_t *handle)
+                                                       sp<ModuleClient> moduleClient,
+                                                       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;
@@ -569,7 +616,8 @@
         return status;
     }
 
-    sp<Model> model = new Model(*handle, session, ioHandle, device, sound_model->type);
+    sp<Model> model = new Model(*handle, session, ioHandle, device, sound_model->type,
+                                moduleClient);
     mModels.replaceValueFor(*handle, model);
 
     return status;
@@ -578,10 +626,6 @@
 status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t handle)
 {
     ALOGV("unloadSoundModel() model handle %d", handle);
-    if (!captureHotwordAllowed()) {
-        return PERMISSION_DENIED;
-    }
-
     AutoMutex lock(mLock);
     return unloadSoundModel_l(handle);
 }
@@ -612,10 +656,6 @@
     if (mHalInterface == 0) {
         return NO_INIT;
     }
-    if (!captureHotwordAllowed()) {
-        return PERMISSION_DENIED;
-    }
-
     if (dataMemory == 0 || dataMemory->pointer() == NULL) {
         ALOGE("startRecognition() dataMemory is 0 or has NULL pointer()");
         return BAD_VALUE;
@@ -668,10 +708,6 @@
     if (mHalInterface == 0) {
         return NO_INIT;
     }
-    if (!captureHotwordAllowed()) {
-        return PERMISSION_DENIED;
-    }
-
     AutoMutex lock(mLock);
     sp<Model> model = getModel(handle);
     if (model == 0) {
@@ -686,7 +722,6 @@
     return NO_ERROR;
 }
 
-
 void SoundTriggerHwService::Module::onCallbackEvent(const sp<CallbackEvent>& event)
 {
     ALOGV("onCallbackEvent type %d", event->mType);
@@ -696,8 +731,8 @@
     if (eventMemory == 0 || eventMemory->pointer() == NULL) {
         return;
     }
-    if (mClient == 0) {
-        ALOGI("%s mClient == 0", __func__);
+    if (mModuleClients.isEmpty()) {
+        ALOGI("%s no clients", __func__);
         return;
     }
 
@@ -720,7 +755,7 @@
 
             recognitionEvent->capture_session = model->mCaptureSession;
             model->mState = Model::STATE_IDLE;
-            client = mClient;
+            client = model->mModuleClient->client();
         }
         if (client != 0) {
             client->onRecognitionEvent(eventMemory);
@@ -737,20 +772,24 @@
                 ALOGW("%s model == 0", __func__);
                 return;
             }
-            client = mClient;
+            client = model->mModuleClient->client();
         }
         if (client != 0) {
             client->onSoundModelEvent(eventMemory);
         }
     } break;
     case CallbackEvent::TYPE_SERVICE_STATE: {
-        sp<ISoundTriggerClient> client;
+        Vector< sp<ISoundTriggerClient> > clients;
         {
             AutoMutex lock(mLock);
-            client = mClient;
+            for (size_t i = 0; i < mModuleClients.size(); i++) {
+                if (mModuleClients[i] != 0) {
+                    clients.add(mModuleClients[i]->client());
+                }
+            }
         }
-        if (client != 0) {
-            client->onServiceStateChange(eventMemory);
+        for (size_t i = 0; i < clients.size(); i++) {
+            clients[i]->onServiceStateChange(eventMemory);
         }
     } break;
     default:
@@ -769,12 +808,6 @@
     return model;
 }
 
-void SoundTriggerHwService::Module::binderDied(
-    const wp<IBinder> &who __unused) {
-    ALOGW("client binder died for module %d", mDescriptor.handle);
-    detach();
-}
-
 // Called with mServiceLock held
 void SoundTriggerHwService::Module::setCaptureState_l(bool active)
 {
@@ -858,8 +891,10 @@
     }
 
     for (size_t i = 0; i < events.size(); i++) {
-        service->sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_RECOGNITION, events[i],
-                                                     this));
+        sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
+                                                            events[i]);
+        callbackEvent->setModule(this);
+        service->sendCallbackEvent_l(callbackEvent);
     }
 
 exit:
@@ -869,17 +904,170 @@
 
 SoundTriggerHwService::Model::Model(sound_model_handle_t handle, audio_session_t session,
                                     audio_io_handle_t ioHandle, audio_devices_t device,
-                                    sound_trigger_sound_model_type_t type) :
+                                    sound_trigger_sound_model_type_t type,
+                                    sp<ModuleClient>& moduleClient) :
     mHandle(handle), mState(STATE_IDLE), mCaptureSession(session),
-    mCaptureIOHandle(ioHandle), mCaptureDevice(device), mType(type)
+    mCaptureIOHandle(ioHandle), mCaptureDevice(device), mType(type),
+    mModuleClient(moduleClient)
 {
-
 }
 
-status_t SoundTriggerHwService::Module::dump(int fd __unused,
-                                             const Vector<String16>& args __unused) {
+#undef LOG_TAG
+#define LOG_TAG "SoundTriggerHwService::ModuleClient"
+
+SoundTriggerHwService::ModuleClient::ModuleClient(const sp<Module>& module,
+                                                  const sp<ISoundTriggerClient>& client)
+ : mModule(module), mClient(client)
+{
+}
+
+void SoundTriggerHwService::ModuleClient::onFirstRef()
+{
+    IInterface::asBinder(mClient)->linkToDeath(this);
+}
+
+SoundTriggerHwService::ModuleClient::~ModuleClient()
+{
+}
+
+status_t SoundTriggerHwService::ModuleClient::dump(int fd __unused,
+                                                   const Vector<String16>& args __unused) {
     String8 result;
     return NO_ERROR;
 }
 
+void SoundTriggerHwService::ModuleClient::detach() {
+    ALOGV("detach()");
+    if (!captureHotwordAllowed()) {
+        return;
+    }
+
+    {
+        AutoMutex lock(mLock);
+        if (mClient != 0) {
+            IInterface::asBinder(mClient)->unlinkToDeath(this);
+            mClient.clear();
+        }
+    }
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return;
+    }
+    module->detach(this);
+}
+
+status_t SoundTriggerHwService::ModuleClient::loadSoundModel(const sp<IMemory>& modelMemory,
+                                sound_model_handle_t *handle)
+{
+    ALOGV("loadSoundModel() handle");
+    if (!captureHotwordAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return NO_INIT;
+    }
+    return module->loadSoundModel(modelMemory, this, handle);
+}
+
+status_t SoundTriggerHwService::ModuleClient::unloadSoundModel(sound_model_handle_t handle)
+{
+    ALOGV("unloadSoundModel() model handle %d", handle);
+    if (!captureHotwordAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return NO_INIT;
+    }
+    return module->unloadSoundModel(handle);
+}
+
+status_t SoundTriggerHwService::ModuleClient::startRecognition(sound_model_handle_t handle,
+                                 const sp<IMemory>& dataMemory)
+{
+    ALOGV("startRecognition() model handle %d", handle);
+    if (!captureHotwordAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return NO_INIT;
+    }
+    return module->startRecognition(handle, dataMemory);
+}
+
+status_t SoundTriggerHwService::ModuleClient::stopRecognition(sound_model_handle_t handle)
+{
+    ALOGV("stopRecognition() model handle %d", handle);
+    if (!captureHotwordAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return NO_INIT;
+    }
+    return module->stopRecognition(handle);
+}
+
+void SoundTriggerHwService::ModuleClient::setCaptureState_l(bool active)
+{
+    ALOGV("ModuleClient::setCaptureState_l %d", active);
+    sp<SoundTriggerHwService> service;
+    sound_trigger_service_state_t state;
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return;
+    }
+    {
+        AutoMutex lock(mLock);
+        state = (active && !module->isConcurrentCaptureAllowed()) ?
+                                        SOUND_TRIGGER_STATE_DISABLED : SOUND_TRIGGER_STATE_ENABLED;
+
+        service = module->service().promote();
+        if (service == 0) {
+            return;
+        }
+    }
+    service->sendServiceStateEvent_l(state, this);
+}
+
+void SoundTriggerHwService::ModuleClient::onCallbackEvent(const sp<CallbackEvent>& event)
+{
+    ALOGV("ModuleClient onCallbackEvent type %d", event->mType);
+
+    sp<IMemory> eventMemory = event->mMemory;
+
+    if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+        return;
+    }
+
+    switch (event->mType) {
+    case CallbackEvent::TYPE_SERVICE_STATE: {
+        sp<ISoundTriggerClient> client;
+        {
+            AutoMutex lock(mLock);
+            client = mClient;
+        }
+        if (client !=0 ) {
+            client->onServiceStateChange(eventMemory);
+        }
+    } break;
+    default:
+        LOG_ALWAYS_FATAL("onCallbackEvent unknown event type %d", event->mType);
+    }
+}
+
+void SoundTriggerHwService::ModuleClient::binderDied(
+    const wp<IBinder> &who __unused) {
+    ALOGW("client binder died for client %p", this);
+    detach();
+}
+
 }; // namespace android
diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h
index 7f7d0cc..60ebb35 100644
--- a/services/soundtrigger/SoundTriggerHwService.h
+++ b/services/soundtrigger/SoundTriggerHwService.h
@@ -39,6 +39,7 @@
     friend class BinderService<SoundTriggerHwService>;
 public:
     class Module;
+    class ModuleClient;
 
     static char const* getServiceName() { return "media.sound_trigger_hw"; }
 
@@ -69,7 +70,8 @@
         };
 
         Model(sound_model_handle_t handle, audio_session_t session, audio_io_handle_t ioHandle,
-              audio_devices_t device, sound_trigger_sound_model_type_t type);
+              audio_devices_t device, sound_trigger_sound_model_type_t type,
+              sp<ModuleClient>& moduleClient);
         ~Model() {}
 
         sound_model_handle_t    mHandle;
@@ -79,6 +81,7 @@
         audio_devices_t         mCaptureDevice;
         sound_trigger_sound_model_type_t mType;
         struct sound_trigger_recognition_config mConfig;
+        sp<ModuleClient>        mModuleClient;
     };
 
     class CallbackEvent : public RefBase {
@@ -88,27 +91,76 @@
             TYPE_SOUNDMODEL,
             TYPE_SERVICE_STATE,
         } event_type;
-        CallbackEvent(event_type type, sp<IMemory> memory, wp<Module> module);
+        CallbackEvent(event_type type, sp<IMemory> memory);
 
         virtual             ~CallbackEvent();
 
+        void setModule(wp<Module> module) { mModule = module; }
+        void setModuleClient(wp<ModuleClient> moduleClient) { mModuleClient = moduleClient; }
+
         event_type mType;
         sp<IMemory> mMemory;
         wp<Module> mModule;
+        wp<ModuleClient> mModuleClient;
     };
 
-    class Module : public virtual RefBase,
-                   public BnSoundTrigger,
-                   public IBinder::DeathRecipient     {
+    class Module : public RefBase {
     public:
 
        Module(const sp<SoundTriggerHwService>& service,
               const sp<SoundTriggerHalInterface>& halInterface,
-              sound_trigger_module_descriptor descriptor,
-              const sp<ISoundTriggerClient>& client);
+              sound_trigger_module_descriptor descriptor);
 
        virtual ~Module();
 
+       virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
+                                       sp<ModuleClient> moduleClient,
+                                       sound_model_handle_t *handle);
+
+       virtual status_t unloadSoundModel(sound_model_handle_t handle);
+
+       virtual status_t startRecognition(sound_model_handle_t handle,
+                                         const sp<IMemory>& dataMemory);
+       virtual status_t stopRecognition(sound_model_handle_t handle);
+
+       sp<SoundTriggerHalInterface> halInterface() const { return mHalInterface; }
+       struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
+       wp<SoundTriggerHwService> service() const { return mService; }
+       bool isConcurrentCaptureAllowed() const { return mDescriptor.properties.concurrent_capture; }
+
+       sp<Model> getModel(sound_model_handle_t handle);
+
+       void setCaptureState_l(bool active);
+
+       sp<ModuleClient> addClient(const sp<ISoundTriggerClient>& client);
+
+       void detach(const sp<ModuleClient>& moduleClient);
+
+       void onCallbackEvent(const sp<CallbackEvent>& event);
+
+    private:
+
+        status_t unloadSoundModel_l(sound_model_handle_t handle);
+
+        Mutex                                  mLock;
+        wp<SoundTriggerHwService>              mService;
+        sp<SoundTriggerHalInterface>           mHalInterface;
+        struct sound_trigger_module_descriptor mDescriptor;
+        Vector< sp<ModuleClient> >             mModuleClients;
+        DefaultKeyedVector< sound_model_handle_t, sp<Model> >     mModels;
+        sound_trigger_service_state_t          mServiceState;
+    }; // class Module
+
+    class ModuleClient : public virtual RefBase,
+                         public BnSoundTrigger,
+                         public IBinder::DeathRecipient {
+    public:
+
+       ModuleClient(const sp<Module>& module,
+              const sp<ISoundTriggerClient>& client);
+
+       virtual ~ModuleClient();
+
        virtual void detach();
 
        virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
@@ -122,35 +174,23 @@
 
        virtual status_t dump(int fd, const Vector<String16>& args);
 
-
-       struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
-       void setClient(const sp<ISoundTriggerClient>& client) { mClient = client; }
-       void clearClient() { mClient.clear(); }
-       sp<ISoundTriggerClient> client() const { return mClient; }
-       wp<SoundTriggerHwService> service() const { return mService; }
-
-       void onCallbackEvent(const sp<CallbackEvent>& event);
-
-       sp<Model> getModel(sound_model_handle_t handle);
-
-       void setCaptureState_l(bool active);
+       virtual void onFirstRef();
 
        // IBinder::DeathRecipient implementation
        virtual void        binderDied(const wp<IBinder> &who);
 
+       void onCallbackEvent(const sp<CallbackEvent>& event);
+
+       void setCaptureState_l(bool active);
+
+       sp<ISoundTriggerClient> client() const { return mClient; }
+
     private:
 
-       status_t unloadSoundModel_l(sound_model_handle_t handle);
-
-
-        Mutex                                  mLock;
-        wp<SoundTriggerHwService>              mService;
-        sp<SoundTriggerHalInterface>           mHalInterface;
-        struct sound_trigger_module_descriptor mDescriptor;
-        sp<ISoundTriggerClient>                mClient;
-        DefaultKeyedVector< sound_model_handle_t, sp<Model> >     mModels;
-        sound_trigger_service_state_t          mServiceState;
-    }; // class Module
+        mutable Mutex               mLock;
+        wp<Module>                  mModule;
+        sp<ISoundTriggerClient>     mClient;
+    }; // class ModuleClient
 
     class CallbackThread : public Thread {
     public:
@@ -175,8 +215,6 @@
         Vector< sp<CallbackEvent> > mEventQueue;
     };
 
-           void detachModule(const sp<Module>& module);
-
     static void recognitionCallback(struct sound_trigger_recognition_event *event, void *cookie);
            sp<IMemory> prepareRecognitionEvent_l(struct sound_trigger_recognition_event *event);
            void sendRecognitionEvent(struct sound_trigger_recognition_event *event, Module *module);
@@ -187,6 +225,8 @@
 
            sp<IMemory> prepareServiceStateEvent_l(sound_trigger_service_state_t state);
            void sendServiceStateEvent_l(sound_trigger_service_state_t state, Module *module);
+           void sendServiceStateEvent_l(sound_trigger_service_state_t state,
+                                        ModuleClient *moduleClient);
 
            void sendCallbackEvent_l(const sp<CallbackEvent>& event);
            void onCallbackEvent(const sp<CallbackEvent>& event);