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> ¶ms,
- 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> ¶ms, OMX::buffer_id *buffer) {
+ OMX_U32 portIndex, const sp<IMemory> ¶ms, 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);