Naming consistency: MediaAnalytics to MediaMetrics
Bug: 145780674
Test: build, boot, dumpsys media.metrics
Test: atest mediametrics_tests CtsNativeMediaMetricsTestCases
Change-Id: Icbfa98da9e61702aee9a1d807b5e126b4b9c6458
diff --git a/media/libmediametrics/include/MediaMetricsItem.h b/media/libmediametrics/include/MediaMetricsItem.h
new file mode 100644
index 0000000..5f33650
--- /dev/null
+++ b/media/libmediametrics/include/MediaMetricsItem.h
@@ -0,0 +1,943 @@
+/*
+ * 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 "MediaMetrics.h"
+
+#include <algorithm>
+#include <string>
+#include <sys/types.h>
+
+#include <cutils/properties.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+class IMediaMetricsService;
+class Parcel;
+
+/*
+ * MediaMetrics Item
+ *
+ * Byte string format.
+ *
+ * For Java
+ * int64 corresponds to long
+ * int32, uint32 corresponds to int
+ * uint16 corresponds to char
+ * uint8, int8 corresponds to byte
+ *
+ * Hence uint8 and uint32 values are limited to INT8_MAX and INT32_MAX.
+ *
+ * Physical layout of integers and doubles within the MediaMetrics byte string
+ * is in Native / host order, which is nearly always little endian.
+ *
+ * -- 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 string which is 0 terminated
+ * (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 cstring, including 0 termination
+ * (int64, int64) for rate
+ * -- end body
+ * -- end of item
+ */
+
+namespace mediametrics {
+
+// Type must match MediaMetrics.java
+enum Type {
+ kTypeNone = 0,
+ kTypeInt32 = 1,
+ kTypeInt64 = 2,
+ kTypeDouble = 3,
+ kTypeCString = 4,
+ kTypeRate = 5,
+};
+
+template<size_t N>
+static inline bool startsWith(const std::string &s, const char (&comp)[N]) {
+ return !strncmp(s.c_str(), comp, N-1);
+}
+
+/**
+ * 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 analytics data
+ static bool isEnabled();
+
+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<IMediaMetricsService> sMediaMetricsService;
+ static sp<IMediaMetricsService> getInstance();
+ static void dropInstance();
+ static bool submitBuffer(const char *buffer, size_t len);
+
+ static status_t writeToByteString(
+ const char *name, int32_t value, char **bufferpptr, char *bufferptrmax);
+ static status_t writeToByteString(
+ const char *name, int64_t value, char **bufferpptr, char *bufferptrmax);
+ static status_t writeToByteString(
+ const char *name, double value, char **bufferpptr, char *bufferptrmax);
+ static status_t writeToByteString(
+ const char *name, const std::pair<int64_t, int64_t> &value,
+ char **bufferpptr, char *bufferptrmax);
+ static status_t writeToByteString(
+ const char *name, char * const &value, char **bufferpptr, char *bufferptrmax);
+ static status_t writeToByteString(
+ const char *name, const char * const &value, char **bufferpptr, char *bufferptrmax);
+ struct none_t {}; // for kTypeNone
+ static status_t writeToByteString(
+ const char *name, const none_t &, char **bufferpptr, char *bufferptrmax);
+
+ template<typename T>
+ static status_t sizeOfByteString(const char *name, const T& value) {
+ return 2 + 1 + strlen(name) + 1 + sizeof(value);
+ }
+ template<> // static
+ status_t sizeOfByteString(const char *name, char * const &value) {
+ return 2 + 1 + strlen(name) + 1 + strlen(value) + 1;
+ }
+ template<> // static
+ status_t sizeOfByteString(const char *name, const char * const &value) {
+ return 2 + 1 + strlen(name) + 1 + strlen(value) + 1;
+ }
+ template<> // static
+ status_t sizeOfByteString(const char *name, const none_t &) {
+ return 2 + 1 + strlen(name) + 1;
+ }
+};
+
+/**
+ * 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<typename T>
+ 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<typename T>
+ 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());
+ }
+
+ 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<typename T>
+ void copyTo(char *ptr, const T& value) {
+ memcpy(ptr, &value, sizeof(value));
+ }
+
+ void init(const char *key) {
+ mBptr = mBegin;
+ const size_t keylen = strlen(key) + 1;
+ 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 media analytics 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 <size_t N = 4096>
+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 : public mediametrics::BaseItem {
+ friend class MediaMetricsJNI; // TODO: remove this access
+
+public:
+
+ // TODO: remove this duplicate definition when frameworks base is updated.
+ enum Type {
+ kTypeNone = 0,
+ kTypeInt32 = 1,
+ kTypeInt64 = 2,
+ kTypeDouble = 3,
+ kTypeCString = 4,
+ kTypeRate = 5,
+ };
+
+ static constexpr const char * const kKeyNone = "none";
+ static constexpr const char * const kKeyAny = "any";
+
+ enum {
+ PROTO_V0 = 0,
+ PROTO_FIRST = PROTO_V0,
+ PROTO_V1 = 1,
+ PROTO_LAST = PROTO_V1,
+ };
+
+ // T must be convertible to mKey
+ template <typename T>
+ explicit Item(T key)
+ : mKey(key) { }
+ Item() = default;
+
+ Item(const Item&) = delete;
+ Item &operator=(const Item&) = delete;
+
+ bool operator==(const Item& other) const {
+ if (mPropCount != other.mPropCount
+ || mPid != other.mPid
+ || mUid != other.mUid
+ || mPkgName != other.mPkgName
+ || mPkgVersionCode != other.mPkgVersionCode
+ || mKey != other.mKey
+ || mTimestamp != other.mTimestamp) return false;
+ for (size_t i = 0; i < mPropCount; ++i) {
+ Prop *p = other.findProp(mProps[i].getName());
+ if (p == nullptr || mProps[i] != *p) return false;
+ }
+ return true;
+ }
+ bool operator!=(const Item& other) const {
+ return !(*this == other);
+ }
+
+ template <typename T>
+ 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();
+
+ // reset all contents, discarding any extra data
+ void clear();
+ Item *dup();
+
+ 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 mPropCount; }
+
+ template<typename S, typename T>
+ Item &set(S key, T value) {
+ allocateProp(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<typename S, typename T>
+ Item &add(S key, T value) {
+ allocateProp(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<typename S, typename T>
+ bool get(S key, T *value) 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<int64_t, int64_t> 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;
+ }
+ // Caller owns the returned string
+ bool getCString(const char *key, char **value) const {
+ const char *cs;
+ if (get(key, &cs)) {
+ *value = cs != nullptr ? strdup(cs) : nullptr;
+ return true;
+ }
+ return false;
+ }
+ bool getString(const char *key, std::string *value) const {
+ return get(key, value);
+ }
+
+ // 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;
+ std::string toString(int version) const;
+ const char *toCString();
+ const char *toCString(int version);
+
+ protected:
+
+ // merge fields from arg into this
+ // with rules for first/last/add, etc
+ // XXX: document semantics and how they are indicated
+ // caller continues to own 'incoming'
+ bool merge(Item *incoming);
+
+private:
+ // handle Parcel version 0
+ int32_t writeToParcel0(Parcel *) const;
+ int32_t readFromParcel0(const Parcel&);
+
+
+
+ // checks equality even with nullptr.
+ static bool stringEquals(const char *a, const char *b) {
+ if (a == nullptr) {
+ return b == nullptr;
+ } else {
+ return b != nullptr && strcmp(a, b) == 0;
+ }
+ }
+
+public:
+
+ class Prop {
+ friend class MediaMetricsJNI; // TODO: remove this access
+ public:
+ Prop() = default;
+ Prop(const Prop& other) {
+ *this = other;
+ }
+ Prop& operator=(const Prop& other) {
+ if (other.mName != nullptr) {
+ mName = strdup(other.mName);
+ } else {
+ mName = nullptr;
+ }
+ mType = other.mType;
+ switch (mType) {
+ case kTypeInt32:
+ u.int32Value = other.u.int32Value;
+ break;
+ case kTypeInt64:
+ u.int64Value = other.u.int64Value;
+ break;
+ case kTypeDouble:
+ u.doubleValue = other.u.doubleValue;
+ break;
+ case kTypeCString:
+ u.CStringValue = strdup(other.u.CStringValue);
+ break;
+ case kTypeRate:
+ u.rate = other.u.rate;
+ break;
+ case kTypeNone:
+ break;
+ default:
+ // abort?
+ break;
+ }
+ return *this;
+ }
+ bool operator==(const Prop& other) const {
+ if (!stringEquals(mName, other.mName)
+ || mType != other.mType) return false;
+ switch (mType) {
+ case kTypeInt32:
+ return u.int32Value == other.u.int32Value;
+ case kTypeInt64:
+ return u.int64Value == other.u.int64Value;
+ case kTypeDouble:
+ return u.doubleValue == other.u.doubleValue;
+ case kTypeCString:
+ return stringEquals(u.CStringValue, other.u.CStringValue);
+ case kTypeRate:
+ return u.rate == other.u.rate;
+ case kTypeNone:
+ default:
+ return true;
+ }
+ }
+ bool operator!=(const Prop& other) const {
+ return !(*this == other);
+ }
+
+ void clear() {
+ free(mName);
+ mName = nullptr;
+ clearValue();
+ }
+ void clearValue() {
+ if (mType == kTypeCString) {
+ free(u.CStringValue);
+ u.CStringValue = nullptr;
+ }
+ mType = kTypeNone;
+ }
+
+ Type getType() const {
+ return mType;
+ }
+
+ const char *getName() const {
+ return mName;
+ }
+
+ void swap(Prop& other) {
+ std::swap(mName, other.mName);
+ std::swap(mType, other.mType);
+ std::swap(u, other.u);
+ }
+
+ void setName(const char *name) {
+ free(mName);
+ if (name != nullptr) {
+ mName = strdup(name);
+ } else {
+ mName = nullptr;
+ }
+ }
+
+ bool isNamed(const char *name) const {
+ return stringEquals(name, mName);
+ }
+
+ template <typename T> void visit(T f) const {
+ switch (mType) {
+ case Item::kTypeInt32:
+ f(u.int32Value);
+ return;
+ case Item::kTypeInt64:
+ f(u.int64Value);
+ return;
+ case Item::kTypeDouble:
+ f(u.doubleValue);
+ return;
+ case Item::kTypeRate:
+ f(u.rate);
+ return;
+ case Item::kTypeCString:
+ f(u.CStringValue);
+ return;
+ default:
+ return;
+ }
+ }
+
+ template <typename T> bool get(T *value) const = delete;
+ template <>
+ bool get(int32_t *value) const {
+ if (mType != kTypeInt32) return false;
+ if (value != nullptr) *value = u.int32Value;
+ return true;
+ }
+ template <>
+ bool get(int64_t *value) const {
+ if (mType != kTypeInt64) return false;
+ if (value != nullptr) *value = u.int64Value;
+ return true;
+ }
+ template <>
+ bool get(double *value) const {
+ if (mType != kTypeDouble) return false;
+ if (value != nullptr) *value = u.doubleValue;
+ return true;
+ }
+ template <>
+ bool get(const char** value) const {
+ if (mType != kTypeCString) return false;
+ if (value != nullptr) *value = u.CStringValue;
+ return true;
+ }
+ template <>
+ bool get(std::string* value) const {
+ if (mType != kTypeCString) return false;
+ if (value != nullptr) *value = u.CStringValue;
+ return true;
+ }
+ template <>
+ bool get(std::pair<int64_t, int64_t> *value) const {
+ if (mType != kTypeRate) return false;
+ if (value != nullptr) {
+ *value = u.rate;
+ }
+ return true;
+ }
+
+ template <typename T> void set(const T& value) = delete;
+ template <>
+ void set(const int32_t& value) {
+ mType = kTypeInt32;
+ u.int32Value = value;
+ }
+ template <>
+ void set(const int64_t& value) {
+ mType = kTypeInt64;
+ u.int64Value = value;
+ }
+ template <>
+ void set(const double& value) {
+ mType = kTypeDouble;
+ u.doubleValue = value;
+ }
+ template <>
+ void set(const char* const& value) {
+ if (mType == kTypeCString) {
+ free(u.CStringValue);
+ } else {
+ mType = kTypeCString;
+ }
+ if (value == nullptr) {
+ u.CStringValue = nullptr;
+ } else {
+ size_t len = strlen(value);
+ if (len > UINT16_MAX - 1) {
+ len = UINT16_MAX - 1;
+ }
+ u.CStringValue = (char *)malloc(len + 1);
+ strncpy(u.CStringValue, value, len);
+ u.CStringValue[len] = 0;
+ }
+ }
+ template <>
+ void set(const std::pair<int64_t, int64_t> &value) {
+ mType = kTypeRate;
+ u.rate = {value.first, value.second};
+ }
+
+ template <typename T> void add(const T& value) = delete;
+ template <>
+ void add(const int32_t& value) {
+ if (mType == kTypeInt32) {
+ u.int32Value += value;
+ } else {
+ mType = kTypeInt32;
+ u.int32Value = value;
+ }
+ }
+ template <>
+ void add(const int64_t& value) {
+ if (mType == kTypeInt64) {
+ u.int64Value += value;
+ } else {
+ mType = kTypeInt64;
+ u.int64Value = value;
+ }
+ }
+ template <>
+ void add(const double& value) {
+ if (mType == kTypeDouble) {
+ u.doubleValue += value;
+ } else {
+ mType = kTypeDouble;
+ u.doubleValue = value;
+ }
+ }
+ template <>
+ void add(const std::pair<int64_t, int64_t>& value) {
+ if (mType == kTypeRate) {
+ u.rate.first += value.first;
+ u.rate.second += value.second;
+ } else {
+ mType = kTypeRate;
+ u.rate = value;
+ }
+ }
+
+ status_t writeToParcel(Parcel *data) const;
+ status_t readFromParcel(const Parcel& data);
+ void toString(char *buffer, size_t length) const;
+ size_t getByteStringSize() const;
+ status_t writeToByteString(char **bufferpptr, char *bufferptrmax) const;
+ status_t readFromByteString(const char **bufferpptr, const char *bufferptrmax);
+
+ // TODO: make private (and consider converting to std::variant)
+ // private:
+ char *mName = nullptr;
+ Type mType = kTypeNone;
+ union u__ {
+ u__() { zero(); }
+ u__(u__ &&other) {
+ *this = std::move(other);
+ }
+ u__& operator=(u__ &&other) {
+ memcpy(this, &other, sizeof(*this));
+ other.zero();
+ return *this;
+ }
+ void zero() { memset(this, 0, sizeof(*this)); }
+
+ int32_t int32Value;
+ int64_t int64Value;
+ double doubleValue;
+ char *CStringValue;
+ std::pair<int64_t, int64_t> rate;
+ } u;
+ };
+
+ class iterator {
+ public:
+ iterator(size_t pos, const Item &_item)
+ : i(std::min(pos, _item.count()))
+ , item(_item) { }
+ iterator &operator++() {
+ i = std::min(i + 1, item.count());
+ return *this;
+ }
+ bool operator!=(iterator &other) const {
+ return i != other.i;
+ }
+ Prop &operator*() const {
+ return item.mProps[i];
+ }
+
+ private:
+ size_t i;
+ const Item &item;
+ };
+
+ iterator begin() const {
+ return iterator(0, *this);
+ }
+ iterator end() const {
+ return iterator(SIZE_MAX, *this);
+ }
+
+private:
+
+ // TODO: make prop management class
+ size_t findPropIndex(const char *name) const;
+ Prop *findProp(const char *name) const;
+ Prop *allocateProp();
+
+ enum {
+ kGrowProps = 10
+ };
+ bool growProps(int increment = kGrowProps);
+ Prop *allocateProp(const char *name);
+ bool removeProp(const char *name);
+ Prop *allocateProp(const std::string& name) { return allocateProp(name.c_str()); }
+
+ size_t mPropCount = 0;
+ size_t mPropSize = 0;
+ Prop *mProps = nullptr;
+
+ pid_t mPid = -1;
+ uid_t mUid = -1;
+ std::string mPkgName;
+ int64_t mPkgVersionCode = 0;
+ std::string mKey{kKeyNone};
+ nsecs_t mTimestamp = 0;
+};
+
+} // namespace mediametrics
+} // namespace android
+
+#endif