/* * 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_MEDIAMETRICSITEM_H #define ANDROID_MEDIA_MEDIAMETRICSITEM_H #include "MediaMetrics.h" #include "MediaMetricsConstants.h" #include #include #include #include #include #include #include #include #include // nsecs_t namespace android { namespace media { class IMediaMetricsService; } class Parcel; /* * MediaMetrics Item * * The MediaMetrics Item allows get/set operations and recording to the service. * * The MediaMetrics LogItem is a faster logging variant. It allows set operations only, * and then recording to the service. * * The Byte String format is as follows: * * For Java * int64 corresponds to long * int32, uint32 corresponds to int * uint16 corresponds to char * uint8, int8 corresponds to byte * * For items transmitted from Java, uint8 and uint32 values are limited * to INT8_MAX and INT32_MAX. This constrains the size of large items * to 2GB, which is consistent with ByteBuffer max size. A native item * can conceivably have size of 4GB. * * Physical layout of integers and doubles within the MediaMetrics byte string * is in Native / host order, which is usually little endian. * * Note that primitive data (ints, doubles) within a Byte String has * no extra padding or alignment requirements, like ByteBuffer. * * -- begin of item * -- begin of header * (uint32) item size: including the item size field * (uint32) header size, including the item size and header size fields. * (uint16) version: exactly 0 * (uint16) key size, that is key strlen + 1 for zero termination. * (int8)+ key, a string which is 0 terminated (UTF-8). * (int32) pid * (int32) uid * (int64) timestamp * -- end of header * -- begin body * (uint32) number of properties * -- repeat for number of properties * (uint16) property size, including property size field itself * (uint8) type of property * (int8)+ key string, including 0 termination * based on type of property (given above), one of: * (int32) * (int64) * (double) * (int8)+ for TYPE_CSTRING, including 0 termination * (int64, int64) for rate * -- end body * -- end of item * * The Byte String format must match MediaMetrics.java. */ namespace mediametrics { // Type must match MediaMetrics.java enum Type { kTypeNone = 0, kTypeInt32 = 1, kTypeInt64 = 2, kTypeDouble = 3, kTypeCString = 4, kTypeRate = 5, }; /* * Helper for status conversions */ inline constexpr const char* statusToStatusString(status_t status) { switch (status) { case BAD_VALUE: return AMEDIAMETRICS_PROP_STATUS_VALUE_ARGUMENT; case DEAD_OBJECT: case FAILED_TRANSACTION: return AMEDIAMETRICS_PROP_STATUS_VALUE_IO; case NO_MEMORY: return AMEDIAMETRICS_PROP_STATUS_VALUE_MEMORY; case PERMISSION_DENIED: return AMEDIAMETRICS_PROP_STATUS_VALUE_SECURITY; case NO_INIT: case INVALID_OPERATION: return AMEDIAMETRICS_PROP_STATUS_VALUE_STATE; case WOULD_BLOCK: return AMEDIAMETRICS_PROP_STATUS_VALUE_TIMEOUT; default: if (status >= 0) return AMEDIAMETRICS_PROP_STATUS_VALUE_OK; // non-negative values "OK" [[fallthrough]]; // negative values are error. case UNKNOWN_ERROR: return AMEDIAMETRICS_PROP_STATUS_VALUE_UNKNOWN; } } status_t statusStringToStatus(const char *error); /* * Time printing * * kPrintFormatLong time string is 19 characters (including null termination). * Example Long Form: "03-27 16:47:06.187" * MM DD HH MM SS MS * * kPrintFormatShort time string is 13 characters (including null termination). * Example Short Form: "16:47:06.187" * HH MM SS MS */ enum PrintFormat { kPrintFormatLong = 0, kPrintFormatShort = 1, }; /** * Converts real time in ns to a time string object, with format similar to logcat. * * \param ns input real time in nanoseconds to convert. * \param buffer the buffer location to put the converted string. * \param bufferSize the size of buffer in bytes. * \param format format, from enum PrintFormat. */ void nsToString( int64_t ns, char *buffer, size_t bufferSize, PrintFormat format = kPrintFormatLong); // Contains the time string struct time_string_t { char time[19]; /* minimum size buffer */ }; /** * Converts real time in ns to a time string object, with format similar to logcat. * * \param ns input real time in nanoseconds to convert. * \param format format, from enum PrintFormat. * \return a time_string_t object with the time string encoded. */ static inline time_string_t timeStringFromNs(int64_t ns, PrintFormat format = kPrintFormatLong) { time_string_t ts; nsToString(ns, ts.time, sizeof(ts.time), format); return ts; } /** * Finds the end of the common time prefix. * * This is as an option to remove the common time prefix to avoid * unnecessary duplicated strings. * * \param time1 a time string from timeStringFromNs * \param time2 a time string from timeStringFromNs * \return the position where the common time prefix ends. For abbreviated * printing of time2, offset the character pointer by this position. */ static inline size_t commonTimePrefixPosition(const char *time1, const char *time2) { size_t i; // Find location of the first mismatch between strings for (i = 0; ; ++i) { if (time1[i] != time2[i]) { break; } if (time1[i] == 0) { return i; // strings match completely } } // Go backwards until we find a delimeter or space. for (; i > 0 && isdigit(time1[i]) // still a number && time1[i - 1] != ' ' ; --i) { } return i; } /** * The MediaMetrics Item has special Item properties, * derived internally or through dedicated setters. * * For consistency we use the following keys to represent * these special Item properties when in a generic Bundle * or in a std::map. * * These values must match MediaMetrics.java */ static inline constexpr const char *BUNDLE_TOTAL_SIZE = "_totalSize"; static inline constexpr const char *BUNDLE_HEADER_SIZE = "_headerSize"; static inline constexpr const char *BUNDLE_VERSION = "_version"; static inline constexpr const char *BUNDLE_KEY_SIZE = "_keySize"; static inline constexpr const char *BUNDLE_KEY = "_key"; static inline constexpr const char *BUNDLE_PID = "_pid"; static inline constexpr const char *BUNDLE_UID = "_uid"; static inline constexpr const char *BUNDLE_TIMESTAMP = "_timestamp"; static inline constexpr const char *BUNDLE_PROPERTY_COUNT = "_propertyCount"; template static inline bool startsWith(const std::string &s, const char (&comp)[N]) { return !strncmp(s.c_str(), comp, N - 1); // last char is null termination } static inline bool startsWith(const std::string& s, const std::string& comp) { return !strncmp(s.c_str(), comp.c_str(), comp.size()); } /** * Defers a function to run in the destructor. * * This helper class is used to log results on exit of a method. */ class Defer { public: template explicit Defer(U &&f) : mThunk(std::forward(f)) {} ~Defer() { mThunk(); } private: const std::function mThunk; }; /** * Media Metrics BaseItem * * A base class which contains utility static functions to write to a byte stream * and access the Media Metrics service. */ class BaseItem { friend class MediaMetricsDeathNotifier; // for dropInstance // enabled 1, disabled 0 public: // are we collecting metrics data static bool isEnabled(); // returns the MediaMetrics service if active. static sp getService(); // submits a raw buffer directly to the MediaMetrics service - this is highly optimized. static status_t submitBuffer(const char *buffer, size_t len); protected: static constexpr const char * const EnabledProperty = "media.metrics.enabled"; static constexpr const char * const EnabledPropertyPersist = "persist.media.metrics.enabled"; static const int EnabledProperty_default = 1; // let's reuse a binder connection static sp sMediaMetricsService; static void dropInstance(); template struct is_item_type { static constexpr inline bool value = std::is_same::value || std::is_same::value || std::is_same::value || std::is_same>:: value || std::is_same::value || std::is_same::value; }; template struct get_type_of { static_assert(is_item_type::value); static constexpr inline Type value = std::is_same::value ? kTypeInt32 : std::is_same::value ? kTypeInt64 : std::is_same::value ? kTypeDouble : std::is_same>:: value ? kTypeRate : std::is_same::value ? kTypeCString : std::is_same::value ? kTypeNone : kTypeNone; }; template static size_t sizeOfByteString(const char *name, const T& value) { static_assert(is_item_type::value); return 2 + 1 + strlen(name) + 1 + sizeof(value); } template <> // static size_t sizeOfByteString(const char *name, const std::string& value) { return 2 + 1 + strlen(name) + 1 + value.size() + 1; } template <> // static size_t sizeOfByteString(const char *name, const std::monostate&) { return 2 + 1 + strlen(name) + 1; } // for speed static size_t sizeOfByteString(const char *name, const char *value) { return 2 + 1 + strlen(name) + 1 + strlen(value) + 1; } template static status_t insert(const T& val, char **bufferpptr, char *bufferptrmax) { static_assert(std::is_trivially_constructible::value); const size_t size = sizeof(val); if (*bufferpptr + size > bufferptrmax) { ALOGE("%s: buffer exceeded with size %zu", __func__, size); return BAD_VALUE; } memcpy(*bufferpptr, &val, size); *bufferpptr += size; return NO_ERROR; } template <> // static status_t insert(const std::string& val, char **bufferpptr, char *bufferptrmax) { const size_t size = val.size() + 1; if (size > UINT16_MAX || *bufferpptr + size > bufferptrmax) { ALOGE("%s: buffer exceeded with size %zu", __func__, size); return BAD_VALUE; } memcpy(*bufferpptr, val.c_str(), size); *bufferpptr += size; return NO_ERROR; } template <> // static status_t insert(const std::pair& val, char **bufferpptr, char *bufferptrmax) { const size_t size = sizeof(val.first) + sizeof(val.second); if (*bufferpptr + size > bufferptrmax) { ALOGE("%s: buffer exceeded with size %zu", __func__, size); return BAD_VALUE; } memcpy(*bufferpptr, &val.first, sizeof(val.first)); memcpy(*bufferpptr + sizeof(val.first), &val.second, sizeof(val.second)); *bufferpptr += size; return NO_ERROR; } template <> // static status_t insert(const std::monostate&, char **, char *) { return NO_ERROR; } // for speed static status_t insert(const char *val, char **bufferpptr, char *bufferptrmax) { const size_t size = strlen(val) + 1; if (size > UINT16_MAX || *bufferpptr + size > bufferptrmax) { ALOGE("%s: buffer exceeded with size %zu", __func__, size); return BAD_VALUE; } memcpy(*bufferpptr, val, size); *bufferpptr += size; return NO_ERROR; } template static status_t writeToByteString( const char *name, const T& value, char **bufferpptr, char *bufferptrmax) { static_assert(is_item_type::value); const size_t len = sizeOfByteString(name, value); if (len > UINT16_MAX) return BAD_VALUE; return insert((uint16_t)len, bufferpptr, bufferptrmax) ?: insert((uint8_t)get_type_of::value, bufferpptr, bufferptrmax) ?: insert(name, bufferpptr, bufferptrmax) ?: insert(value, bufferpptr, bufferptrmax); } // for speed static status_t writeToByteString( const char *name, const char *value, char **bufferpptr, char *bufferptrmax) { const size_t len = sizeOfByteString(name, value); if (len > UINT16_MAX) return BAD_VALUE; return insert((uint16_t)len, bufferpptr, bufferptrmax) ?: insert((uint8_t)kTypeCString, bufferpptr, bufferptrmax) ?: insert(name, bufferpptr, bufferptrmax) ?: insert(value, bufferpptr, bufferptrmax); } template static void toStringBuffer( const char *name, const T& value, char *buffer, size_t length) = delete; template <> // static void toStringBuffer( const char *name, const int32_t& value, char *buffer, size_t length) { snprintf(buffer, length, "%s=%d", name, value); } template <> // static void toStringBuffer( const char *name, const int64_t& value, char *buffer, size_t length) { snprintf(buffer, length, "%s=%lld", name, (long long)value); } template <> // static void toStringBuffer( const char *name, const double& value, char *buffer, size_t length) { snprintf(buffer, length, "%s=%e", name, value); } template <> // static void toStringBuffer( const char *name, const std::pair& value, char *buffer, size_t length) { snprintf(buffer, length, "%s=%lld/%lld", name, (long long)value.first, (long long)value.second); } template <> // static void toStringBuffer( const char *name, const std::string& value, char *buffer, size_t length) { // TODO sanitize string for ':' '=' snprintf(buffer, length, "%s=%s", name, value.c_str()); } template <> // static void toStringBuffer( const char *name, const std::monostate&, char *buffer, size_t length) { snprintf(buffer, length, "%s=()", name); } template static status_t writeToParcel( const char *name, const T& value, Parcel *parcel) = delete; template <> // static status_t writeToParcel( const char *name, const int32_t& value, Parcel *parcel) { return parcel->writeCString(name) ?: parcel->writeInt32(get_type_of::value) ?: parcel->writeInt32(value); } template <> // static status_t writeToParcel( const char *name, const int64_t& value, Parcel *parcel) { return parcel->writeCString(name) ?: parcel->writeInt32(get_type_of::value) ?: parcel->writeInt64(value); } template <> // static status_t writeToParcel( const char *name, const double& value, Parcel *parcel) { return parcel->writeCString(name) ?: parcel->writeInt32(get_type_of::value) ?: parcel->writeDouble(value); } template <> // static status_t writeToParcel( const char *name, const std::pair& value, Parcel *parcel) { return parcel->writeCString(name) ?: parcel->writeInt32(get_type_of< std::pair>::value) ?: parcel->writeInt64(value.first) ?: parcel->writeInt64(value.second); } template <> // static status_t writeToParcel( const char *name, const std::string& value, Parcel *parcel) { return parcel->writeCString(name) ?: parcel->writeInt32(get_type_of::value) ?: parcel->writeCString(value.c_str()); } template <> // static status_t writeToParcel( const char *name, const std::monostate&, Parcel *parcel) { return parcel->writeCString(name) ?: parcel->writeInt32(get_type_of::value); } template static status_t extract(T *val, const char **bufferpptr, const char *bufferptrmax) { static_assert(std::is_trivially_constructible::value); const size_t size = sizeof(*val); if (*bufferpptr + size > bufferptrmax) { ALOGE("%s: buffer exceeded with size %zu", __func__, size); return BAD_VALUE; } memcpy(val, *bufferpptr, size); *bufferpptr += size; return NO_ERROR; } template <> // static status_t extract(std::string *val, const char **bufferpptr, const char *bufferptrmax) { const char *ptr = *bufferpptr; do { if (ptr >= bufferptrmax) { ALOGE("%s: buffer exceeded", __func__); android_errorWriteLog(0x534e4554, "204445255"); return BAD_VALUE; } } while (*ptr++ != 0); // ptr is terminator+1, == bufferptrmax if we finished entire buffer *val = *bufferpptr; *bufferpptr = ptr; return NO_ERROR; } template <> // static status_t extract(std::pair *val, const char **bufferpptr, const char *bufferptrmax) { const size_t size = sizeof(val->first) + sizeof(val->second); if (*bufferpptr + size > bufferptrmax) { ALOGE("%s: buffer exceeded with size %zu", __func__, size); return BAD_VALUE; } memcpy(&val->first, *bufferpptr, sizeof(val->first)); memcpy(&val->second, *bufferpptr + sizeof(val->first), sizeof(val->second)); *bufferpptr += size; return NO_ERROR; } template <> // static status_t extract(std::monostate *, const char **, const char *) { return NO_ERROR; } }; /** * Media Metrics BufferedItem * * A base class which represents a put-only Media Metrics item, storing * the Media Metrics data in a buffer with begin and end pointers. * * If a property key is entered twice, it will be stored in the buffer twice, * and (implementation defined) the last value for that key will be used * by the Media Metrics service. * * For realloc, a baseRealloc pointer must be passed in either explicitly * or implicitly in the constructor. This will be updated with the value used on realloc. */ class BufferedItem : public BaseItem { public: static inline constexpr uint16_t kVersion = 0; virtual ~BufferedItem() = default; BufferedItem(const BufferedItem&) = delete; BufferedItem& operator=(const BufferedItem&) = delete; BufferedItem(const std::string& key, char *begin, char *end) : BufferedItem(key.c_str(), begin, end) { } BufferedItem(const char *key, char *begin, char *end) : BufferedItem(key, begin, end, nullptr) { } BufferedItem(const char *key, char **begin, char *end) : BufferedItem(key, *begin, end, begin) { } BufferedItem(const char *key, char *begin, char *end, char **baseRealloc) : mBegin(begin) , mEnd(end) , mBaseRealloc(baseRealloc) { init(key); } template BufferedItem &set(const char *key, const T& value) { reallocFor(sizeOfByteString(key, value)); if (mStatus == NO_ERROR) { mStatus = BaseItem::writeToByteString(key, value, &mBptr, mEnd); ++mPropCount; } return *this; } template BufferedItem &set(const std::string& key, const T& value) { return set(key.c_str(), value); } BufferedItem &setPid(pid_t pid) { if (mStatus == NO_ERROR) { copyTo(mBegin + mHeaderLen - 16, (int32_t)pid); } return *this; } BufferedItem &setUid(uid_t uid) { if (mStatus == NO_ERROR) { copyTo(mBegin + mHeaderLen - 12, (int32_t)uid); } return *this; } BufferedItem &setTimestamp(nsecs_t timestamp) { if (mStatus == NO_ERROR) { copyTo(mBegin + mHeaderLen - 8, (int64_t)timestamp); } return *this; } bool record() { return updateHeader() && BaseItem::submitBuffer(getBuffer(), getLength()) == OK; } bool isValid () const { return mStatus == NO_ERROR; } char *getBuffer() const { return mBegin; } size_t getLength() const { return mBptr - mBegin; } size_t getRemaining() const { return mEnd - mBptr; } size_t getCapacity() const { return mEnd - mBegin; } bool updateHeader() { if (mStatus != NO_ERROR) return false; copyTo(mBegin + 0, (uint32_t)getLength()); copyTo(mBegin + 4, (uint32_t)mHeaderLen); copyTo(mBegin + mHeaderLen, (uint32_t)mPropCount); return true; } protected: BufferedItem() = default; void reallocFor(size_t required) { if (mStatus != NO_ERROR) return; const size_t remaining = getRemaining(); if (required <= remaining) return; if (mBaseRealloc == nullptr) { mStatus = NO_MEMORY; return; } const size_t current = getLength(); size_t minimum = current + required; if (minimum > SSIZE_MAX >> 1) { mStatus = NO_MEMORY; return; } minimum <<= 1; void *newptr = realloc(*mBaseRealloc, minimum); if (newptr == nullptr) { mStatus = NO_MEMORY; return; } if (newptr != *mBaseRealloc) { // ALOGD("base changed! current:%zu new size %zu", current, minimum); if (*mBaseRealloc == nullptr) { memcpy(newptr, mBegin, current); } mBegin = (char *)newptr; *mBaseRealloc = mBegin; mEnd = mBegin + minimum; mBptr = mBegin + current; } else { // ALOGD("base kept! current:%zu new size %zu", current, minimum); mEnd = mBegin + minimum; } } template void copyTo(char *ptr, const T& value) { memcpy(ptr, &value, sizeof(value)); } void init(const char *key) { mBptr = mBegin; const size_t keylen = key == nullptr ? 0 : strlen(key) + 1; if (keylen <= 1) { mStatus = BAD_VALUE; // prevent null pointer or empty keys. return; } mHeaderLen = 4 + 4 + 2 + 2 + keylen + 4 + 4 + 8; reallocFor(mHeaderLen); if (mStatus != NO_ERROR) return; mBptr = mBegin + mHeaderLen + 4; // this includes propcount. if (mEnd < mBptr || keylen > UINT16_MAX) { mStatus = NO_MEMORY; mBptr = mEnd; return; } copyTo(mBegin + 8, kVersion); copyTo(mBegin + 10, (uint16_t)keylen); strcpy(mBegin + 12, key); // initialize some parameters (that could be overridden) setPid(-1); setUid(-1); setTimestamp(0); } char *mBegin = nullptr; char *mEnd = nullptr; char **mBaseRealloc = nullptr; // set to an address if realloc should be done. // upon return, that pointer is updated with // whatever needs to be freed. char *mBptr = nullptr; status_t mStatus = NO_ERROR; uint32_t mPropCount = 0; uint32_t mHeaderLen = 0; }; /** * MediaMetrics LogItem is a stack allocated mediametrics item used for * fast logging. It falls over to a malloc if needed. * * This is templated with a buffer size to allocate on the stack. */ template class LogItem : public BufferedItem { public: explicit LogItem(const std::string& key) : LogItem(key.c_str()) { } // Since this class will not be defined before the base class, we initialize variables // in our own order. explicit LogItem(const char *key) { mBegin = mBuffer; mEnd = mBuffer + N; mBaseRealloc = &mReallocPtr; init(key); } ~LogItem() override { if (mReallocPtr != nullptr) { // do the check before calling free to avoid overhead. free(mReallocPtr); } } private: char *mReallocPtr = nullptr; // set non-null by base class if realloc happened. char mBuffer[N]; }; /** * Media Metrics Item * * A mutable item representing an event or record that will be * logged with the Media Metrics service. For client logging, one should * use the mediametrics::Item. * * The Item is designed for the service as it has getters. */ class Item final : public mediametrics::BaseItem { public: class Prop { public: using Elem = std::variant< std::monostate, // kTypeNone int32_t, // kTypeInt32 int64_t, // kTypeInt64 double, // kTypeDouble std::string, // kTypeCString std::pair // kTypeRate >; Prop() = default; Prop(const Prop& other) { *this = other; } Prop& operator=(const Prop& other) { mName = other.mName; mElem = other.mElem; return *this; } Prop(Prop&& other) noexcept { *this = std::move(other); } Prop& operator=(Prop&& other) noexcept { mName = std::move(other.mName); mElem = std::move(other.mElem); return *this; } bool operator==(const Prop& other) const { return mName == other.mName && mElem == other.mElem; } bool operator!=(const Prop& other) const { return !(*this == other); } void clear() { mName.clear(); mElem = std::monostate{}; } void clearValue() { mElem = std::monostate{}; } const char *getName() const { return mName.c_str(); } void swap(Prop& other) { std::swap(mName, other.mName); std::swap(mElem, other.mElem); } void setName(const char *name) { mName = name; } bool isNamed(const char *name) const { return mName == name; } template void visit(T f) const { std::visit(f, mElem); } template bool get(T *value) const { auto pval = std::get_if(&mElem); if (pval != nullptr) { *value = *pval; return true; } return false; } const Elem& get() const { return mElem; } template void set(const T& value) { mElem = value; } template void add(const T& value) { auto pval = std::get_if(&mElem); if (pval != nullptr) { *pval += value; } else { mElem = value; } } template <> void add(const std::pair& value) { auto pval = std::get_if>(&mElem); if (pval != nullptr) { pval->first += value.first; pval->second += value.second; } else { mElem = value; } } status_t writeToParcel(Parcel *parcel) const { return std::visit([this, parcel](auto &value) { return BaseItem::writeToParcel(mName.c_str(), value, parcel);}, mElem); } void toStringBuffer(char *buffer, size_t length) const { return std::visit([this, buffer, length](auto &value) { BaseItem::toStringBuffer(mName.c_str(), value, buffer, length);}, mElem); } size_t getByteStringSize() const { return std::visit([this](auto &value) { return BaseItem::sizeOfByteString(mName.c_str(), value);}, mElem); } status_t writeToByteString(char **bufferpptr, char *bufferptrmax) const { return std::visit([this, bufferpptr, bufferptrmax](auto &value) { return BaseItem::writeToByteString(mName.c_str(), value, bufferpptr, bufferptrmax); }, mElem); } status_t readFromParcel(const Parcel& data); status_t readFromByteString(const char **bufferpptr, const char *bufferptrmax); private: std::string mName; Elem mElem; }; // Iteration of props within item class iterator { public: explicit iterator(const std::map::const_iterator &_it) : it(_it) { } iterator &operator++() { ++it; return *this; } bool operator!=(iterator &other) const { return it != other.it; } const Prop &operator*() const { return it->second; } private: std::map::const_iterator it; }; iterator begin() const { return iterator(mProps.cbegin()); } iterator end() const { return iterator(mProps.cend()); } // T must be convertible to mKey template explicit Item(T key) : mKey(key) { } Item() = default; // We enable default copy and move constructors and make this class final // to prevent a derived class; this avoids possible data slicing. Item(const Item& other) = default; Item(Item&& other) = default; Item& operator=(const Item& other) = default; Item& operator=(Item&& other) = default; bool operator==(const Item& other) const { return mPid == other.mPid && mUid == other.mUid && mPkgName == other.mPkgName && mPkgVersionCode == other.mPkgVersionCode && mKey == other.mKey && mTimestamp == other.mTimestamp && mProps == other.mProps ; } bool operator!=(const Item& other) const { return !(*this == other); } template static Item* create(T key) { return new Item(key); } static Item* create() { return new Item(); } static Item* convert(mediametrics_handle_t); static mediametrics_handle_t convert(Item *); // access functions for the class ~Item(); void clear() { mPid = -1; mUid = -1; mPkgName.clear(); mPkgVersionCode = 0; mTimestamp = 0; mKey.clear(); mProps.clear(); } Item *dup() const { return new Item(*this); } Item &setKey(const char *key) { mKey = key; return *this; } const std::string& getKey() const { return mKey; } // # of properties in the record size_t count() const { return mProps.size(); } template Item &set(S key, T value) { findOrAllocateProp(key).set(value); return *this; } // set values appropriately Item &setInt32(const char *key, int32_t value) { return set(key, value); } Item &setInt64(const char *key, int64_t value) { return set(key, value); } Item &setDouble(const char *key, double value) { return set(key, value); } Item &setRate(const char *key, int64_t count, int64_t duration) { return set(key, std::make_pair(count, duration)); } Item &setCString(const char *key, const char *value) { return set(key, value); } // fused get/add/set; if attr wasn't there, it's a simple set. // type-mismatch counts as "wasn't there". template Item &add(S key, T value) { findOrAllocateProp(key).add(value); return *this; } Item &addInt32(const char *key, int32_t value) { return add(key, value); } Item &addInt64(const char *key, int64_t value) { return add(key, value); } Item &addDouble(const char *key, double value) { return add(key, value); } Item &addRate(const char *key, int64_t count, int64_t duration) { return add(key, std::make_pair(count, duration)); } // find & extract values // return indicates whether attr exists (and thus value filled in) // NULL parameter value suppresses storage of value. template bool get(S key, T *value) const { const Prop *prop = findProp(key); return prop != nullptr && prop->get(value); } bool getInt32(const char *key, int32_t *value) const { return get(key, value); } bool getInt64(const char *key, int64_t *value) const { return get(key, value); } bool getDouble(const char *key, double *value) const { return get(key, value); } bool getRate(const char *key, int64_t *count, int64_t *duration, double *rate) const { std::pair value; if (!get(key, &value)) return false; if (count != nullptr) *count = value.first; if (duration != nullptr) *duration = value.second; if (rate != nullptr) { if (value.second != 0) { *rate = (double)value.first / value.second; // TODO: isn't INF OK? } else { *rate = 0.; } } return true; } bool getString(const char *key, std::string *value) const { return get(key, value); } // Caller owns the returned string bool getCString(const char *key, char **value) const { std::string s; if (get(key, &s)) { *value = strdup(s.c_str()); return true; } return false; } const Prop::Elem* get(const char *key) const { const Prop *prop = findProp(key); return prop == nullptr ? nullptr : &prop->get(); } // Deliver the item to MediaMetrics 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' size_t filter(size_t count, const char *attrs[]); size_t filterNot(size_t count, const char *attrs[]); size_t filter(const char *attr) { return filter(1, &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' Item &setTimestamp(nsecs_t); nsecs_t getTimestamp() const; Item &setPid(pid_t); pid_t getPid() const; Item &setUid(uid_t); uid_t getUid() const; Item &setPkgName(const std::string &pkgName); std::string getPkgName() const { return mPkgName; } Item &setPkgVersionCode(int64_t); int64_t getPkgVersionCode() const; // our serialization code for binder calls status_t writeToParcel(Parcel *) const; status_t readFromParcel(const Parcel&); status_t writeToByteString(char **bufferptr, size_t *length) const; status_t readFromByteString(const char *bufferptr, size_t length); std::string toString() const; const char *toCString(); /** * Returns true if the item has a property with a target value. * * If propName is nullptr, hasPropElem() returns false. * * \param propName is the property name. * \param elem is the value to match. std::monostate matches any. */ bool hasPropElem(const char *propName, const Prop::Elem& elem) const { if (propName == nullptr) return false; const Prop::Elem *e = get(propName); return e != nullptr && (std::holds_alternative(elem) || elem == *e); } /** * Returns -2, -1, 0 (success) if the item has a property (wildcard matched) with a * target value. * * The enum RecursiveWildcardCheck designates the meaning of the returned value. * * RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD = -2, * RECURSIVE_WILDCARD_CHECK_NO_MATCH_WILDCARD_FOUND = -1, * RECURSIVE_WILDCARD_CHECK_MATCH_FOUND = 0. * * If url is nullptr, RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD is returned. * * \param url is the full item + property name, which may have wildcards '*' * denoting an arbitrary sequence of 0 or more characters. * \param elem is the target property value to match. std::monostate matches any. * \return 0 if the property was matched, * -1 if the property was not matched and a wildcard char was encountered, * -2 if the property was not matched with no wildcard char encountered. */ enum RecursiveWildcardCheck { RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD = -2, RECURSIVE_WILDCARD_CHECK_NO_MATCH_WILDCARD_FOUND = -1, RECURSIVE_WILDCARD_CHECK_MATCH_FOUND = 0, }; enum RecursiveWildcardCheck recursiveWildcardCheckElem( const char *url, const Prop::Elem& elem) const { if (url == nullptr) return RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD; return recursiveWildcardCheckElem(getKey().c_str(), url, elem); } private: enum RecursiveWildcardCheck recursiveWildcardCheckElem( const char *itemKeyPtr, const char *url, const Prop::Elem& elem) const { for (; *url && *itemKeyPtr; ++url, ++itemKeyPtr) { if (*url != *itemKeyPtr) { if (*url == '*') { // wildcard ++url; while (true) { if (recursiveWildcardCheckElem(itemKeyPtr, url, elem) == RECURSIVE_WILDCARD_CHECK_MATCH_FOUND) { return RECURSIVE_WILDCARD_CHECK_MATCH_FOUND; } if (*itemKeyPtr == 0) break; ++itemKeyPtr; } return RECURSIVE_WILDCARD_CHECK_NO_MATCH_WILDCARD_FOUND; } return RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD; } } if (itemKeyPtr[0] != 0 || url[0] != '.') { return RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD; } const char *propName = url + 1; // skip the '.' return hasPropElem(propName, elem) ? RECURSIVE_WILDCARD_CHECK_MATCH_FOUND : RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD; } // handle Parcel version 0 int32_t writeToParcel0(Parcel *) const; int32_t readFromParcel0(const Parcel&); const Prop *findProp(const char *key) const { auto it = mProps.find(key); return it != mProps.end() ? &it->second : nullptr; } Prop &findOrAllocateProp(const char *key) { auto it = mProps.find(key); if (it != mProps.end()) return it->second; Prop &prop = mProps[key]; prop.setName(key); return prop; } // Changes to member variables below require changes to clear(). pid_t mPid = -1; uid_t mUid = -1; std::string mPkgName; int64_t mPkgVersionCode = 0; std::string mKey; nsecs_t mTimestamp = 0; std::map mProps; }; } // namespace mediametrics } // namespace android #endif // ANDROID_MEDIA_MEDIAMETRICSITEM_H