Refine MediaAnalytics framework
Rework some interfaces to avoid unnecessary sp<> mechanisms;
document which side of caller/callee owns pointers afterwards.
Rework internal attribute representation, add support routines.
Bug: 33547720
Test: Boot, ran subset of CTS media tests
Change-Id: Id0d65bd6a847704dc98d38be9aa5e1ce63c20e1c
diff --git a/include/media/IMediaAnalyticsService.h b/include/media/IMediaAnalyticsService.h
index 21da6ad..9213637 100644
--- a/include/media/IMediaAnalyticsService.h
+++ b/include/media/IMediaAnalyticsService.h
@@ -49,14 +49,16 @@
// '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;
+ // caller continues to own the passed item
+ virtual MediaAnalyticsItem::SessionID_t submit(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;
+ // caller responsible for deallocating returned data structures
+ virtual List<MediaAnalyticsItem *> *getMediaAnalyticsItemList(bool finished, int64_t ts) = 0;
+ virtual List<MediaAnalyticsItem *> *getMediaAnalyticsItemList(bool finished, int64_t ts, MediaAnalyticsItem::Key key) = 0;
};
diff --git a/include/media/MediaAnalyticsItem.h b/include/media/MediaAnalyticsItem.h
index 73c9dd4..a78aa8b 100644
--- a/include/media/MediaAnalyticsItem.h
+++ b/include/media/MediaAnalyticsItem.h
@@ -36,13 +36,22 @@
// the class interface
//
-class MediaAnalyticsItem : public RefBase {
+class MediaAnalyticsItem {
friend class MediaAnalyticsService;
friend class IMediaAnalyticsService;
public:
+ enum Type {
+ kTypeNone = 0,
+ kTypeInt32 = 1,
+ kTypeInt64 = 2,
+ kTypeDouble = 3,
+ kTypeCString = 4,
+ kTypeRate = 5,
+ };
+
// sessionid
// unique within device, within boot,
typedef int64_t SessionID_t;
@@ -61,7 +70,7 @@
// Attr: names for attributes within a record
// format "prop1" or "prop/subprop"
// XXX: need to better define the format
- typedef AString Attr;
+ typedef const char *Attr;
public:
@@ -87,6 +96,7 @@
// reset all contents, discarding any extra data
void clear();
+ MediaAnalyticsItem *dup();
// set the key discriminator for the record.
// most often initialized as part of the constructor
@@ -97,28 +107,32 @@
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);
+ void setInt32(Attr, int32_t value);
+ void setInt64(Attr, int64_t value);
+ void setDouble(Attr, double value);
+ void setRate(Attr, int64_t count, int64_t duration);
+ void 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);
+ void addInt32(Attr, int32_t value);
+ void addInt64(Attr, int64_t value);
+ void addDouble(Attr, double value);
+ void addRate(Attr, int64_t count, int64_t duration);
// find & extract values
// return indicates whether attr exists (and thus value filled in)
+ // NULL parameter value suppresses storage of value.
bool getInt32(Attr, int32_t *value);
bool getInt64(Attr, int64_t *value);
bool getDouble(Attr, double *value);
+ bool getRate(Attr, int64_t *count, int64_t *duration, double *rate);
+ // Caller owns the returned string
bool getCString(Attr, char **value);
// parameter indicates whether to close any existing open
// record with same key before establishing a new record
+ // caller retains ownership of 'this'.
bool selfrecord(bool);
bool selfrecord();
@@ -135,7 +149,7 @@
// clients need not worry about these.
// timestamp, pid, and uid only used on server side
- // timestamp is in 'nanoseconds, unix time'
+ // timestamp is in 'nanoseconds, unix time'
MediaAnalyticsItem &setTimestamp(nsecs_t);
nsecs_t getTimestamp() const;
@@ -159,7 +173,8 @@
// 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> );
+ // caller continues to own 'incoming'
+ bool merge(MediaAnalyticsItem *incoming);
// enabled 1, disabled 0
static const char * const EnabledProperty;
@@ -185,31 +200,36 @@
Key mKey;
- class Item : public RefBase {
-
- public:
-
- enum Type {
- kTypeNone = 0,
- kTypeInt32 = 1,
- kTypeInt64 = 2,
- kTypeDouble = 3,
- kTypeCString = 4,
- };
-
- Item();
- ~Item();
- void clear();
+ struct Prop {
Type mType;
+ const char *mName;
+ size_t mNameLen; // the strlen(), doesn't include the null
union {
int32_t int32Value;
int64_t int64Value;
double doubleValue;
char *CStringValue;
+ struct { int64_t count, duration; } rate;
} u;
+ void setName(const char *name, size_t len);
};
- KeyedVector<Attr, sp<Item>> mItems;
+
+ void initProp(Prop *item);
+ void clearProp(Prop *item);
+ void clearPropValue(Prop *item);
+ void copyProp(Prop *dst, const Prop *src);
+ enum {
+ kGrowProps = 10
+ };
+ void growProps(int increment = kGrowProps);
+ size_t findPropIndex(const char *name, size_t len);
+ Prop *findProp(const char *name);
+ Prop *allocateProp(const char *name);
+
+ size_t mPropCount;
+ size_t mPropSize;
+ Prop *mProps;
};
diff --git a/media/libmedia/IMediaAnalyticsService.cpp b/media/libmedia/IMediaAnalyticsService.cpp
index afe9c36..cc4aa35 100644
--- a/media/libmedia/IMediaAnalyticsService.cpp
+++ b/media/libmedia/IMediaAnalyticsService.cpp
@@ -80,7 +80,7 @@
return sessionid;
}
- virtual MediaAnalyticsItem::SessionID_t submit(sp<MediaAnalyticsItem> item, bool forcenew)
+ virtual MediaAnalyticsItem::SessionID_t submit(MediaAnalyticsItem *item, bool forcenew)
{
// have this record submit itself
// this will be a binder call with appropriate timing
@@ -115,12 +115,12 @@
return sessionid;
}
- virtual List<sp<MediaAnalyticsItem>> *getMediaAnalyticsItemList(bool finished, nsecs_t ts)
+ virtual List<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)
+ virtual List<MediaAnalyticsItem*> *getMediaAnalyticsItemList(bool finished, nsecs_t ts, MediaAnalyticsItem::Key key)
{
Parcel data, reply;
status_t err;
@@ -134,18 +134,18 @@
}
data.writeCString(str);
err = remote()->transact(GET_ITEM_LIST, data, &reply);
- if (err != NO_ERROR) {
- return NULL;
- }
+ if (err != NO_ERROR) {
+ return NULL;
+ }
// read a count
int32_t count = reply.readInt32();
- List<sp<MediaAnalyticsItem>> *list = NULL;
+ List<MediaAnalyticsItem*> *list = NULL;
if (count > 0) {
- list = new List<sp<MediaAnalyticsItem>>();
+ list = new List<MediaAnalyticsItem*>();
for (int i=0;i<count;i++) {
- sp<MediaAnalyticsItem> item = new MediaAnalyticsItem;
+ MediaAnalyticsItem *item = new MediaAnalyticsItem();
// XXX: watch for failures here
item->readFromParcel(reply);
list->push_back(item);
@@ -190,14 +190,14 @@
CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
bool forcenew;
- sp<MediaAnalyticsItem> item = new MediaAnalyticsItem;
+ MediaAnalyticsItem *item = new MediaAnalyticsItem;
data.readBool(&forcenew);
item->readFromParcel(data);
item->setPid(clientPid);
- // submit() takes ownership of / responsibility for the item
+ // submit() takes over ownership of 'item'
MediaAnalyticsItem::SessionID_t sessionid = submit(item, forcenew);
reply->writeInt64(sessionid);
@@ -212,11 +212,11 @@
MediaAnalyticsItem::Key key = data.readCString();
// find the (0 or more) items
- List<sp<MediaAnalyticsItem>> *list = getMediaAnalyticsItemList(finished, ts, key);
+ List<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();
+ for (List<MediaAnalyticsItem*>::iterator it = list->begin();
it != list->end(); it++) {
(*it)->writeToParcel(reply);
}
diff --git a/media/libmedia/MediaAnalyticsItem.cpp b/media/libmedia/MediaAnalyticsItem.cpp
index 5f05d5a..76397c7 100644
--- a/media/libmedia/MediaAnalyticsItem.cpp
+++ b/media/libmedia/MediaAnalyticsItem.cpp
@@ -17,14 +17,15 @@
#undef LOG_TAG
#define LOG_TAG "MediaAnalyticsItem"
-#include <sys/types.h>
#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.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>
@@ -37,6 +38,12 @@
namespace android {
#define DEBUG_SERVICEACCESS 0
+#define DEBUG_API 0
+#define DEBUG_ALLOCATIONS 0
+
+// after this many failed attempts, we stop trying [from this process] and just say that
+// the service is off.
+#define SVC_TRIES 2
// the few universal keys we have
const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyAny = "any";
@@ -49,29 +56,81 @@
// access functions for the class
MediaAnalyticsItem::MediaAnalyticsItem()
- : RefBase(),
- mPid(0),
+ : mPid(0),
mUid(0),
mSessionID(MediaAnalyticsItem::SessionIDNone),
mTimestamp(0),
- mFinalized(0) {
+ mFinalized(0),
+ mPropCount(0), mPropSize(0), mProps(NULL)
+{
mKey = MediaAnalyticsItem::kKeyNone;
}
MediaAnalyticsItem::MediaAnalyticsItem(MediaAnalyticsItem::Key key)
- : RefBase(),
- mPid(0),
+ : mPid(0),
mUid(0),
mSessionID(MediaAnalyticsItem::SessionIDNone),
mTimestamp(0),
- mFinalized(0) {
+ mFinalized(0),
+ mPropCount(0), mPropSize(0), mProps(NULL)
+{
+ if (DEBUG_ALLOCATIONS) {
+ ALOGD("Allocate MediaAnalyticsItem @ %p", this);
+ }
mKey = key;
}
MediaAnalyticsItem::~MediaAnalyticsItem() {
+ if (DEBUG_ALLOCATIONS) {
+ ALOGD("Destroy MediaAnalyticsItem @ %p", this);
+ }
clear();
}
+void MediaAnalyticsItem::clear() {
+
+ // clean allocated storage from key
+ mKey.clear();
+
+ // clean attributes
+ // contents of the attributes
+ for (size_t i = 0 ; i < mPropSize; i++ ) {
+ clearProp(&mProps[i]);
+ }
+ // the attribute records themselves
+ if (mProps != NULL) {
+ free(mProps);
+ mProps = NULL;
+ }
+ mPropSize = 0;
+ mPropCount = 0;
+
+ return;
+}
+
+// make a deep copy of myself
+MediaAnalyticsItem *MediaAnalyticsItem::dup() {
+ MediaAnalyticsItem *dst = new MediaAnalyticsItem(this->mKey);
+
+ if (dst != NULL) {
+ // key as part of constructor
+ dst->mPid = this->mPid;
+ dst->mUid = this->mUid;
+ dst->mSessionID = this->mSessionID;
+ dst->mTimestamp = this->mTimestamp;
+ dst->mFinalized = this->mFinalized;
+
+ // properties aka attributes
+ dst->growProps(this->mPropCount);
+ for(size_t i=0;i<mPropCount;i++) {
+ copyProp(&dst->mProps[i], &this->mProps[i]);
+ }
+ dst->mPropCount = this->mPropCount;
+ }
+
+ return dst;
+}
+
// so clients can send intermediate values to be overlaid later
MediaAnalyticsItem &MediaAnalyticsItem::setFinalized(bool value) {
mFinalized = value;
@@ -92,11 +151,10 @@
}
MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::generateSessionID() {
- MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
- ALOGD("generateSessionID()");
if (mSessionID == SessionIDNone) {
// get one from the server
+ MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
sp<IMediaAnalyticsService> svc = getInstance();
if (svc != NULL) {
newid = svc->generateUniqueSessionID();
@@ -139,31 +197,8 @@
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
+// this key is for the overall record -- "codec", "player", "drm", etc
MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
- // XXX: possible validation of legal keys.
mKey = key;
return *this;
}
@@ -172,179 +207,221 @@
return mKey;
}
-// number of keys we have in our dictionary
-// we won't upload empty records
+// number of attributes we have in this record
int32_t MediaAnalyticsItem::count() const {
- return mItems.size();
+ return mPropCount;
+}
+
+// find the proper entry in the list
+size_t MediaAnalyticsItem::findPropIndex(const char *name, size_t len)
+{
+ size_t i = 0;
+ for (; i < mPropCount; i++) {
+ Prop *prop = &mProps[i];
+ if (prop->mNameLen != len) {
+ continue;
+ }
+ if (memcmp(name, prop->mName, len) == 0) {
+ break;
+ }
+ }
+ return i;
+}
+
+MediaAnalyticsItem::Prop *MediaAnalyticsItem::findProp(const char *name) {
+ size_t len = strlen(name);
+ size_t i = findPropIndex(name, len);
+ if (i < mPropCount) {
+ return &mProps[i];
+ }
+ return NULL;
+}
+
+void MediaAnalyticsItem::Prop::setName(const char *name, size_t len) {
+ mNameLen = len;
+ mName = (const char *) malloc(len+1);
+ memcpy ((void *)mName, name, len+1);
+}
+
+// used only as part of a storing operation
+MediaAnalyticsItem::Prop *MediaAnalyticsItem::allocateProp(const char *name) {
+ size_t len = strlen(name);
+ size_t i = findPropIndex(name, len);
+ Prop *prop;
+
+ if (i < mPropCount) {
+ prop = &mProps[i];
+ } else {
+ if (i == mPropSize) {
+ growProps();
+ // XXX: verify success
+ }
+ i = mPropCount++;
+ prop = &mProps[i];
+ prop->setName(name, len);
+ }
+
+ return prop;
}
// 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;
+void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
+ Prop *prop = allocateProp(name);
+ prop->mType = kTypeInt32;
+ prop->u.int32Value = value;
}
-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;
+void MediaAnalyticsItem::setInt64(MediaAnalyticsItem::Attr name, int64_t value) {
+ Prop *prop = allocateProp(name);
+ prop->mType = kTypeInt64;
+ prop->u.int64Value = value;
}
-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;
+void MediaAnalyticsItem::setDouble(MediaAnalyticsItem::Attr name, double value) {
+ Prop *prop = allocateProp(name);
+ prop->mType = kTypeDouble;
+ prop->u.doubleValue = value;
}
-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;
+void MediaAnalyticsItem::setCString(MediaAnalyticsItem::Attr name, const char *value) {
+
+ Prop *prop = allocateProp(name);
+ // any old value will be gone
+ prop->mType = kTypeCString;
+ prop->u.CStringValue = strdup(value);
}
+void MediaAnalyticsItem::setRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
+ Prop *prop = allocateProp(name);
+ prop->mType = kTypeRate;
+ prop->u.rate.count = count;
+ prop->u.rate.duration = duration;
+}
+
+
// 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;
+void MediaAnalyticsItem::addInt32(MediaAnalyticsItem::Attr name, int32_t value) {
+ Prop *prop = allocateProp(name);
+ switch (prop->mType) {
+ case kTypeInt32:
+ prop->u.int32Value += value;
+ break;
+ default:
+ clearPropValue(prop);
+ prop->mType = kTypeInt32;
+ prop->u.int32Value = value;
+ break;
}
- 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;
+void MediaAnalyticsItem::addInt64(MediaAnalyticsItem::Attr name, int64_t value) {
+ Prop *prop = allocateProp(name);
+ switch (prop->mType) {
+ case kTypeInt64:
+ prop->u.int64Value += value;
+ break;
+ default:
+ clearPropValue(prop);
+ prop->mType = kTypeInt64;
+ prop->u.int64Value = value;
+ break;
}
- 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;
+void MediaAnalyticsItem::addRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
+ Prop *prop = allocateProp(name);
+ switch (prop->mType) {
+ case kTypeRate:
+ prop->u.rate.count += count;
+ prop->u.rate.duration += duration;
+ break;
+ default:
+ clearPropValue(prop);
+ prop->mType = kTypeRate;
+ prop->u.rate.count = count;
+ prop->u.rate.duration = duration;
+ break;
}
- 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;
+}
+
+void MediaAnalyticsItem::addDouble(MediaAnalyticsItem::Attr name, double value) {
+ Prop *prop = allocateProp(name);
+ switch (prop->mType) {
+ case kTypeDouble:
+ prop->u.doubleValue += value;
+ break;
+ default:
+ clearPropValue(prop);
+ prop->mType = kTypeDouble;
+ prop->u.doubleValue = value;
+ break;
}
- 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) {
+bool MediaAnalyticsItem::getInt32(MediaAnalyticsItem::Attr name, int32_t *value) {
+ Prop *prop = findProp(name);
+ if (prop == NULL || prop->mType != kTypeInt32) {
return false;
}
- sp<Item> &item = mItems.editValueAt(i);
- *value = item->u.int32Value;
+ if (value != NULL) {
+ *value = prop->u.int32Value;
+ }
return true;
}
-bool MediaAnalyticsItem::getInt64(MediaAnalyticsItem::Attr attr, int64_t *value) {
- ssize_t i = mItems.indexOfKey(attr);
- if (i < 0) {
+
+bool MediaAnalyticsItem::getInt64(MediaAnalyticsItem::Attr name, int64_t *value) {
+ Prop *prop = findProp(name);
+ if (prop == NULL || prop->mType != kTypeInt64) {
return false;
}
- sp<Item> &item = mItems.editValueAt(i);
- *value = item->u.int64Value;
+ if (value != NULL) {
+ *value = prop->u.int64Value;
+ }
return true;
}
-bool MediaAnalyticsItem::getDouble(MediaAnalyticsItem::Attr attr, double *value) {
- ssize_t i = mItems.indexOfKey(attr);
- if (i < 0) {
+
+bool MediaAnalyticsItem::getRate(MediaAnalyticsItem::Attr name, int64_t *count, int64_t *duration, double *rate) {
+ Prop *prop = findProp(name);
+ if (prop == NULL || prop->mType != kTypeRate) {
return false;
}
- sp<Item> &item = mItems.editValueAt(i);
- *value = item->u.doubleValue;
+ if (count != NULL) {
+ *count = prop->u.rate.count;
+ }
+ if (duration != NULL) {
+ *duration = prop->u.rate.duration;
+ }
+ if (rate != NULL) {
+ double r = 0.0;
+ if (prop->u.rate.duration != 0) {
+ r = prop->u.rate.count / (double) prop->u.rate.duration;
+ }
+ *rate = r;
+ }
+ return true;
+}
+
+bool MediaAnalyticsItem::getDouble(MediaAnalyticsItem::Attr name, double *value) {
+ Prop *prop = findProp(name);
+ if (prop == NULL || prop->mType != kTypeDouble) {
+ return false;
+ }
+ if (value != NULL) {
+ *value = prop->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) {
+bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr name, char **value) {
+ Prop *prop = findProp(name);
+ if (prop == NULL || prop->mType != kTypeDouble) {
return false;
}
- sp<Item> &item = mItems.editValueAt(i);
- char *p = strdup(item->u.CStringValue);
- *value = p;
+ if (value != NULL) {
+ *value = strdup(prop->u.CStringValue);
+ }
return true;
}
@@ -352,17 +429,27 @@
// 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) {
+ if (attrs == NULL || 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);
+ const char *name = attrs[i];
+ size_t len = strlen(name);
+ size_t j = findPropIndex(name, len);
+ if (j >= mPropCount) {
+ // not there
+ continue;
+ } else if (j+1 == mPropCount) {
+ // last one, shorten
zapped++;
+ clearProp(&mProps[j]);
+ mPropCount--;
+ } else {
+ // in the middle, bring last one down and shorten
+ zapped++;
+ clearProp(&mProps[j]);
+ mProps[j] = mProps[mPropCount-1];
+ mPropCount--;
}
}
return zapped;
@@ -372,19 +459,20 @@
// return value is # keys removed
int32_t MediaAnalyticsItem::filterNot(int n, MediaAnalyticsItem::Attr attrs[]) {
int zapped = 0;
- if (attrs == NULL) {
+ if (attrs == NULL || n <= 0) {
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);
+ for (ssize_t i = mPropCount-1 ; i >=0 ; i--) {
+ Prop *prop = &mProps[i];
+ for (ssize_t j = 0; j < n ; j++) {
+ if (strcmp(prop->mName, attrs[j]) == 0) {
+ clearProp(prop);
zapped++;
+ if (i != (ssize_t)(mPropCount-1)) {
+ *prop = mProps[mPropCount-1];
+ }
+ initProp(&mProps[mPropCount-1]);
+ mPropCount--;
break;
}
}
@@ -394,36 +482,79 @@
// 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;
+int32_t MediaAnalyticsItem::filter(MediaAnalyticsItem::Attr name) {
+ return filter(1, &name);
}
-
// handle individual items/properties stored within the class
//
-MediaAnalyticsItem::Item::Item()
- : mType(kTypeNone)
-{
-}
-MediaAnalyticsItem::Item::~Item()
-{
- clear();
-}
+void MediaAnalyticsItem::initProp(Prop *prop) {
+ if (prop != NULL) {
+ prop->mName = NULL;
+ prop->mNameLen = 0;
-void MediaAnalyticsItem::Item::clear()
-{
- if (mType == kTypeCString && u.CStringValue != NULL) {
- free(u.CStringValue);
- u.CStringValue = NULL;
+ prop->mType = kTypeNone;
}
- mType = kTypeNone;
+}
+
+void MediaAnalyticsItem::clearProp(Prop *prop)
+{
+ if (prop != NULL) {
+ if (prop->mName != NULL) {
+ free((void *)prop->mName);
+ prop->mName = NULL;
+ prop->mNameLen = 0;
+ }
+
+ clearPropValue(prop);
+ }
+}
+
+void MediaAnalyticsItem::clearPropValue(Prop *prop)
+{
+ if (prop != NULL) {
+ if (prop->mType == kTypeCString && prop->u.CStringValue != NULL) {
+ free(prop->u.CStringValue);
+ prop->u.CStringValue = NULL;
+ }
+ prop->mType = kTypeNone;
+ }
+}
+
+void MediaAnalyticsItem::copyProp(Prop *dst, const Prop *src)
+{
+ // get rid of any pointers in the dst
+ clearProp(dst);
+
+ *dst = *src;
+
+ // fix any pointers that we blindly copied, so we have our own copies
+ if (dst->mName) {
+ void *p = malloc(dst->mNameLen + 1);
+ memcpy (p, src->mName, dst->mNameLen + 1);
+ dst->mName = (const char *) p;
+ }
+ if (dst->mType == kTypeCString) {
+ dst->u.CStringValue = strdup(src->u.CStringValue);
+ }
+}
+
+void MediaAnalyticsItem::growProps(int increment)
+{
+ if (increment <= 0) {
+ increment = kGrowProps;
+ }
+ int nsize = mPropSize + increment;
+ Prop *ni = (Prop *)realloc(mProps, sizeof(Prop) * nsize);
+
+ if (ni != NULL) {
+ for (int i = mPropSize; i < nsize; i++) {
+ initProp(&ni[i]);
+ }
+ mProps = ni;
+ mPropSize = nsize;
+ }
}
// Parcel / serialize things for binder calls
@@ -442,18 +573,25 @@
MediaAnalyticsItem::Attr attr = data.readCString();
int32_t ztype = data.readInt32();
switch (ztype) {
- case MediaAnalyticsItem::Item::kTypeInt32:
+ case MediaAnalyticsItem::kTypeInt32:
setInt32(attr, data.readInt32());
break;
- case MediaAnalyticsItem::Item::kTypeInt64:
+ case MediaAnalyticsItem::kTypeInt64:
setInt64(attr, data.readInt64());
break;
- case MediaAnalyticsItem::Item::kTypeDouble:
+ case MediaAnalyticsItem::kTypeDouble:
setDouble(attr, data.readDouble());
break;
- case MediaAnalyticsItem::Item::kTypeCString:
+ case MediaAnalyticsItem::kTypeCString:
setCString(attr, data.readCString());
break;
+ case MediaAnalyticsItem::kTypeRate:
+ {
+ int64_t count = data.readInt64();
+ int64_t duration = data.readInt64();
+ setRate(attr, count, duration);
+ }
+ break;
default:
ALOGE("reading bad item type: %d, idx %d",
ztype, i);
@@ -474,32 +612,33 @@
data->writeInt64(mTimestamp);
// set of items
- int count = mItems.size();
+ int count = mPropCount;
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;
- }
+ Prop *prop = &mProps[i];
+ data->writeCString(prop->mName);
+ data->writeInt32(prop->mType);
+ switch (prop->mType) {
+ case MediaAnalyticsItem::kTypeInt32:
+ data->writeInt32(prop->u.int32Value);
+ break;
+ case MediaAnalyticsItem::kTypeInt64:
+ data->writeInt64(prop->u.int64Value);
+ break;
+ case MediaAnalyticsItem::kTypeDouble:
+ data->writeDouble(prop->u.doubleValue);
+ break;
+ case MediaAnalyticsItem::kTypeRate:
+ data->writeInt64(prop->u.rate.count);
+ data->writeInt64(prop->u.rate.duration);
+ break;
+ case MediaAnalyticsItem::kTypeCString:
+ data->writeCString(prop->u.CStringValue);
+ break;
+ default:
+ ALOGE("found bad Prop type: %d, idx %d, name %s",
+ prop->mType, i, prop->mName);
+ break;
}
}
@@ -511,11 +650,13 @@
AString MediaAnalyticsItem::toString() {
AString result = "(";
- char buffer[256];
+ char buffer[512];
// same order as we spill into the parcel, although not required
// key+session are our primary matching criteria
+ //RBE ALOGD("mKey.c_str");
result.append(mKey.c_str());
+ //RBE ALOGD("post-mKey.c_str");
result.append(":");
snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
result.append(buffer);
@@ -530,37 +671,40 @@
result.append(buffer);
// set of items
- int count = mItems.size();
+ int count = mPropCount;
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:
+ Prop *prop = &mProps[i];
+ switch (prop->mType) {
+ case MediaAnalyticsItem::kTypeInt32:
snprintf(buffer,sizeof(buffer),
- "%s=%d:", attr.c_str(), value->u.int32Value);
+ "%s=%d:", prop->mName, prop->u.int32Value);
break;
- case MediaAnalyticsItem::Item::kTypeInt64:
+ case MediaAnalyticsItem::kTypeInt64:
snprintf(buffer,sizeof(buffer),
- "%s=%" PRId64 ":", attr.c_str(), value->u.int64Value);
+ "%s=%" PRId64 ":", prop->mName, prop->u.int64Value);
break;
- case MediaAnalyticsItem::Item::kTypeDouble:
+ case MediaAnalyticsItem::kTypeDouble:
snprintf(buffer,sizeof(buffer),
- "%s=%e:", attr.c_str(), value->u.doubleValue);
+ "%s=%e:", prop->mName, prop->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());
+ case MediaAnalyticsItem::kTypeRate:
+ snprintf(buffer,sizeof(buffer),
+ "%s=%" PRId64 "/%" PRId64 ":", prop->mName,
+ prop->u.rate.count, prop->u.rate.duration);
+ break;
+ case MediaAnalyticsItem::kTypeCString:
+ snprintf(buffer,sizeof(buffer), "%s=", prop->mName);
result.append(buffer);
- result.append(value->u.CStringValue);
+ // XXX: sanitize string for ':' '='
+ result.append(prop->u.CStringValue);
buffer[0] = ':';
buffer[1] = '\0';
break;
default:
- ALOGE("to_String bad item type: %d",
- value->mType);
+ ALOGE("to_String bad item type: %d for %s",
+ prop->mType, prop->mName);
break;
}
result.append(buffer);
@@ -579,8 +723,10 @@
bool MediaAnalyticsItem::selfrecord(bool forcenew) {
- AString p = this->toString();
- ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
+ if (DEBUG_API) {
+ AString p = this->toString();
+ ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
+ }
sp<IMediaAnalyticsService> svc = getInstance();
@@ -588,6 +734,8 @@
svc->submit(this, forcenew);
return true;
} else {
+ AString p = this->toString();
+ ALOGD("Unable to record: %s [forcenew=%d]", p.c_str(), forcenew);
return false;
}
}
@@ -616,6 +764,7 @@
//static
sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
static const char *servicename = "media.analytics";
+ static int tries_remaining = SVC_TRIES;
int enabled = isEnabled();
if (enabled == false) {
@@ -629,8 +778,10 @@
Mutex::Autolock _l(sInitMutex);
const char *badness = "";
-
- if (sAnalyticsService == NULL) {
+ // think of tries_remaining as telling us whether service==NULL because
+ // (1) we haven't tried to initialize it yet
+ // (2) we've tried to initialize it, but failed.
+ if (sAnalyticsService == NULL && tries_remaining > 0) {
sp<IServiceManager> sm = defaultServiceManager();
if (sm != NULL) {
sp<IBinder> binder = sm->getService(String16(servicename));
@@ -642,13 +793,17 @@
} else {
badness = "No Service Manager access";
}
- // always
- if (1 || DEBUG_SERVICEACCESS) {
- if (sAnalyticsService == NULL) {
+
+ if (sAnalyticsService == NULL) {
+ if (tries_remaining > 0) {
+ tries_remaining--;
+ }
+ if (DEBUG_SERVICEACCESS) {
ALOGD("Unable to bind to service %s: %s", servicename, badness);
}
}
}
+
return sAnalyticsService;
}
}
@@ -656,7 +811,7 @@
// 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) {
+bool MediaAnalyticsItem::merge(MediaAnalyticsItem *incoming) {
// if I don't have key or session id, take them from incoming
// 'this' should never be missing both of them...
@@ -670,28 +825,35 @@
setFinalized(incoming->getFinalized());
// for each attribute from 'incoming', resolve appropriately
- int nattr = incoming->mItems.size();
+ int nattr = incoming->mPropCount;
for (int i = 0 ; i < nattr; i++ ) {
- const MediaAnalyticsItem::Attr attr = incoming->mItems.keyAt(i);
- const sp<Item> value = incoming->mItems.valueAt(i);
+ Prop *iprop = &incoming->mProps[i];
+ Prop *oprop = findProp(iprop->mName);
+ const char *p = iprop->mName;
+ size_t len = strlen(p);
+ char semantic = p[len-1];
- const char *p = attr.c_str();
- char semantic = p[strlen(p)-1];
+ if (oprop == NULL) {
+ // no oprop, so we insert the new one
+ oprop = allocateProp(p);
+ copyProp(oprop, iprop);
+ } else {
+ // merge iprop into oprop
+ switch (semantic) {
+ case '<': // first aka keep old)
+ /* nop */
+ break;
- switch (semantic) {
- default: // default operation is keep new
- case '>': // last aka keep new
- mItems.replaceValueFor(attr, value);
- break;
+ default: // default is 'last'
+ case '>': // last (aka keep new)
+ copyProp(oprop, iprop);
+ break;
- case '<': /* first aka keep first*/
- /* nop */
- break;
+ case '+': /* sum */
+ // XXX validate numeric types, sum in place
+ break;
- case '+': /* sum */
- // XXX validate numeric types, sum in place
- break;
-
+ }
}
}
diff --git a/media/libmediaanalyticsservice/MediaAnalyticsService.cpp b/media/libmediaanalyticsservice/MediaAnalyticsService.cpp
index a039c6c..1d0246d 100644
--- a/media/libmediaanalyticsservice/MediaAnalyticsService.cpp
+++ b/media/libmediaanalyticsservice/MediaAnalyticsService.cpp
@@ -74,7 +74,7 @@
namespace android {
-static int trackqueue = 0;
+#define DEBUG_QUEUE 0
//using android::status_t;
//using android::OK;
@@ -94,8 +94,8 @@
ALOGD("MediaAnalyticsService created");
// clear our queues
- mOpen = new List<sp<MediaAnalyticsItem>>();
- mFinalized = new List<sp<MediaAnalyticsItem>>();
+ mOpen = new List<MediaAnalyticsItem *>();
+ mFinalized = new List<MediaAnalyticsItem *>();
mItemsSubmitted = 0;
mItemsFinalized = 0;
@@ -120,7 +120,8 @@
return (++mLastSessionID);
}
-MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(sp<MediaAnalyticsItem> item, bool forcenew) {
+// caller surrenders ownership of 'item'
+MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(MediaAnalyticsItem *item, bool forcenew) {
MediaAnalyticsItem::SessionID_t id = MediaAnalyticsItem::SessionIDInvalid;
@@ -136,6 +137,7 @@
// validate the record; we discard if we don't like it
if (contentValid(item) == false) {
+ delete item;
return MediaAnalyticsItem::SessionIDInvalid;
}
@@ -155,13 +157,17 @@
bool finalizing = item->getFinalized();
// if finalizing, we'll remove it
- sp<MediaAnalyticsItem> oitem = findItem(mOpen, item, finalizing | forcenew);
+ 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) {
+ if (oitem->count() == 0) {
+ // we're responsible for disposing of the dead record
+ delete oitem;
+ oitem = NULL;
+ } else {
oitem->setFinalized(true);
saveItem(mFinalized, oitem, 0);
}
@@ -181,50 +187,53 @@
mItemsFinalized++;
}
id = oitem->getSessionID();
+
+ // we're responsible for disposing of the dead record
+ delete item;
+ item = NULL;
}
} else {
- // nothing to merge, save the new record
- if (finalizing) {
- if (item->count() != 0) {
- // drop empty records
- saveItem(mFinalized, item, 0);
- mItemsFinalized++;
- }
+ // nothing to merge, save the new record
+ id = item->getSessionID();
+ if (finalizing) {
+ if (item->count() == 0) {
+ // drop empty records
+ delete item;
+ item = NULL;
} else {
- saveItem(mOpen, item, 1);
+ saveItem(mFinalized, item, 0);
+ mItemsFinalized++;
}
- id = item->getSessionID();
+ } else {
+ saveItem(mOpen, item, 1);
+ }
}
-
return id;
}
-List<sp<MediaAnalyticsItem>> *MediaAnalyticsService::getMediaAnalyticsItemList(bool finished, nsecs_t ts) {
+List<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<MediaAnalyticsItem*> *list;
list = getMediaAnalyticsItemList(finished, ts, MediaAnalyticsItem::kKeyAny);
return list;
}
-List<sp<MediaAnalyticsItem>> *MediaAnalyticsService::getMediaAnalyticsItemList(bool , nsecs_t , MediaAnalyticsItem::Key ) {
+List<MediaAnalyticsItem *> *MediaAnalyticsService::getMediaAnalyticsItemList(bool , nsecs_t , MediaAnalyticsItem::Key ) {
// XXX: implement the get-item-list semantics
- List<sp<MediaAnalyticsItem>> *list = NULL;
+ List<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>&)
+status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
{
- const size_t SIZE = 256;
+ const size_t SIZE = 512;
char buffer[SIZE];
String8 result;
@@ -234,52 +243,100 @@
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;
}
+
+ // crack any parameters
+ bool clear = false;
+ nsecs_t ts_since = 0;
+ String16 clearOption("-clear");
+ String16 sinceOption("-since");
+ int n = args.size();
+ for (int i = 0; i < n; i++) {
+ String8 myarg(args[i]);
+ if (args[i] == clearOption) {
+ clear = true;
+ } else if (args[i] == sinceOption) {
+ i++;
+ if (i < n) {
+ String8 value(args[i]);
+ char *endp;
+ const char *p = value.string();
+ ts_since = strtoll(p, &endp, 10);
+ if (endp == p || *endp != '\0') {
+ ts_since = 0;
+ }
+ } else {
+ ts_since = 0;
+ }
+ }
+ }
+
+ 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);
+ if (ts_since != 0) {
+ snprintf(buffer, SIZE,
+ "Dumping Queue entries more recent than: %" PRId64 "\n",
+ (int64_t) ts_since);
+ 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, ts_since));
+
+ snprintf(buffer, sizeof(buffer), "\nIn-Progress Analytics (newest first):\n");
+ result.append(buffer);
+ result.append(this->dumpQueue(mOpen, ts_since));
+
+ // 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
+
+ if (clear) {
+ // remove everything from the finalized queue
+ while (mFinalized->size() > 0) {
+ MediaAnalyticsItem * oitem = *(mFinalized->begin());
+ if (DEBUG_QUEUE) {
+ ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
+ oitem->getKey().c_str(), oitem->getSessionID(),
+ oitem->getTimestamp());
+ }
+ mFinalized->erase(mFinalized->begin());
+ mItemsDiscarded++;
+ }
+ }
+
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;
+String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList) {
+ return dumpQueue(theList, (nsecs_t) 0);
+}
+
+String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since) {
+ const size_t SIZE = 512;
char buffer[SIZE];
String8 result;
int slot = 0;
@@ -287,12 +344,20 @@
if (theList->empty()) {
result.append("empty\n");
} else {
- List<sp<MediaAnalyticsItem>>::iterator it = theList->begin();
- for (; it != theList->end(); it++, slot++) {
+ List<MediaAnalyticsItem *>::iterator it = theList->begin();
+ for (; it != theList->end(); it++) {
+ nsecs_t when = (*it)->getTimestamp();
+ if (when < ts_since) {
+ continue;
+ }
AString entry = (*it)->toString();
- snprintf(buffer, sizeof(buffer), "%4d: %s\n",
+ snprintf(buffer, sizeof(buffer), "%4d: %s",
slot, entry.c_str());
result.append(buffer);
+ buffer[0] = '\n';
+ buffer[1] = '\0';
+ result.append(buffer);
+ slot++;
}
}
@@ -304,15 +369,13 @@
// XXX: rewrite this to manage persistence, etc.
// insert appropriately into queue
-void MediaAnalyticsService::saveItem(List<sp<MediaAnalyticsItem>> *l, sp<MediaAnalyticsItem> item, int front) {
+void MediaAnalyticsService::saveItem(List<MediaAnalyticsItem *> *l, MediaAnalyticsItem * item, int front) {
Mutex::Autolock _l(mLock);
- if (false)
+ if (DEBUG_QUEUE) {
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());
}
@@ -324,7 +387,7 @@
l->push_back(item);
}
- if (trackqueue) {
+ if (DEBUG_QUEUE) {
String8 after = dumpQueue(l);
ALOGD("Q after insert: %s", after.string());
}
@@ -332,25 +395,28 @@
// 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) {
+ MediaAnalyticsItem * oitem = *(l->begin());
+ if (DEBUG_QUEUE) {
ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
oitem->getKey().c_str(), oitem->getSessionID(),
oitem->getTimestamp());
}
l->erase(l->begin());
- mItemsDiscarded++;
+ ALOGD("drop record at %s:%d", __FILE__, __LINE__);
+ delete oitem;
+ ALOGD("[done] drop record at %s:%d", __FILE__, __LINE__);
+ mItemsDiscarded++;
}
}
- if (trackqueue) {
+ if (DEBUG_QUEUE) {
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) {
+static bool compatibleItems(MediaAnalyticsItem * oitem, MediaAnalyticsItem * nitem) {
if (0) {
ALOGD("Compare: o %s n %s",
@@ -386,18 +452,18 @@
}
// find the incomplete record that this will overlay
-sp<MediaAnalyticsItem> MediaAnalyticsService::findItem(List<sp<MediaAnalyticsItem>> *theList, sp<MediaAnalyticsItem> nitem, bool removeit) {
- sp<MediaAnalyticsItem> item;
-
+MediaAnalyticsItem *MediaAnalyticsService::findItem(List<MediaAnalyticsItem*> *theList, MediaAnalyticsItem *nitem, bool removeit) {
if (nitem == NULL) {
return NULL;
}
+ MediaAnalyticsItem *item = NULL;
+
Mutex::Autolock _l(mLock);
- for (List<sp<MediaAnalyticsItem>>::iterator it = theList->begin();
+ for (List<MediaAnalyticsItem *>::iterator it = theList->begin();
it != theList->end(); it++) {
- sp<MediaAnalyticsItem> tmp = (*it);
+ MediaAnalyticsItem *tmp = (*it);
if (!compatibleItems(tmp, nitem)) {
continue;
@@ -415,33 +481,37 @@
// delete the indicated record
-void MediaAnalyticsService::deleteItem(List<sp<MediaAnalyticsItem>> *l, sp<MediaAnalyticsItem> item) {
+void MediaAnalyticsService::deleteItem(List<MediaAnalyticsItem *> *l, MediaAnalyticsItem *item) {
Mutex::Autolock _l(mLock);
- if(trackqueue) {
+ if(DEBUG_QUEUE) {
String8 before = dumpQueue(l);
ALOGD("Q before delete: %s", before.string());
}
- for (List<sp<MediaAnalyticsItem>>::iterator it = l->begin();
+ for (List<MediaAnalyticsItem *>::iterator it = l->begin();
it != l->end(); it++) {
if ((*it)->getSessionID() != item->getSessionID())
continue;
- ALOGD(" --- removing record for SessionID %" PRId64 "", item->getSessionID());
+ if (DEBUG_QUEUE) {
+ ALOGD(" --- removing record for SessionID %" PRId64 "", item->getSessionID());
+ ALOGD("drop record at %s:%d", __FILE__, __LINE__);
+ }
+ delete *it;
l->erase(it);
break;
}
- if (trackqueue) {
+ if (DEBUG_QUEUE) {
String8 after = dumpQueue(l);
ALOGD("Q after delete: %s", after.string());
}
}
// are the contents good
-bool MediaAnalyticsService::contentValid(sp<MediaAnalyticsItem>) {
+bool MediaAnalyticsService::contentValid(MediaAnalyticsItem *) {
// certain keys require certain uids
// internal consistency
@@ -450,7 +520,7 @@
}
// are we rate limited, normally false
-bool MediaAnalyticsService::rateLimited(sp<MediaAnalyticsItem>) {
+bool MediaAnalyticsService::rateLimited(MediaAnalyticsItem *) {
return false;
}
diff --git a/media/libmediaanalyticsservice/MediaAnalyticsService.h b/media/libmediaanalyticsservice/MediaAnalyticsService.h
index f9afeb2..3e2298f 100644
--- a/media/libmediaanalyticsservice/MediaAnalyticsService.h
+++ b/media/libmediaanalyticsservice/MediaAnalyticsService.h
@@ -36,11 +36,12 @@
public:
- virtual int64_t submit(sp<MediaAnalyticsItem> item, bool forcenew);
+ // on this side, caller surrenders ownership
+ virtual int64_t submit(MediaAnalyticsItem *item, bool forcenew);
- virtual List<sp<MediaAnalyticsItem>>
+ virtual List<MediaAnalyticsItem *>
*getMediaAnalyticsItemList(bool finished, int64_t ts);
- virtual List<sp<MediaAnalyticsItem>>
+ virtual List<MediaAnalyticsItem *>
*getMediaAnalyticsItemList(bool finished, int64_t ts, MediaAnalyticsItem::Key key);
@@ -68,24 +69,25 @@
int32_t mMaxRecords;
// input validation after arrival from client
- bool contentValid(sp<MediaAnalyticsItem>);
- bool rateLimited(sp<MediaAnalyticsItem>);
+ bool contentValid(MediaAnalyticsItem *);
+ bool rateLimited(MediaAnalyticsItem *);
// the ones that are still open
// (newest at front) since we keep looking for them
- List<sp<MediaAnalyticsItem>> *mOpen;
+ List<MediaAnalyticsItem *> *mOpen;
// the ones we've finalized
// (oldest at front) so it prints nicely for dumpsys
- List<sp<MediaAnalyticsItem>> *mFinalized;
+ List<MediaAnalyticsItem *> *mFinalized;
// searching within these queues: queue, key
- sp<MediaAnalyticsItem> findItem(List<sp<MediaAnalyticsItem>> *,
- sp<MediaAnalyticsItem>, bool removeit);
+ MediaAnalyticsItem *findItem(List<MediaAnalyticsItem *> *,
+ MediaAnalyticsItem *, bool removeit);
- void saveItem(sp<MediaAnalyticsItem>);
- void saveItem(List<sp<MediaAnalyticsItem>>*, sp<MediaAnalyticsItem>, int);
- void deleteItem(List<sp<MediaAnalyticsItem>>*, sp<MediaAnalyticsItem>);
+ void saveItem(MediaAnalyticsItem);
+ void saveItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *, int);
+ void deleteItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *);
- String8 dumpQueue(List<sp<MediaAnalyticsItem>> *);
+ String8 dumpQueue(List<MediaAnalyticsItem*> *);
+ String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t);
};
diff --git a/services/mediaanalytics/main_mediaanalytics.cpp b/services/mediaanalytics/main_mediaanalytics.cpp
index ba601ee..672d13d 100644
--- a/services/mediaanalytics/main_mediaanalytics.cpp
+++ b/services/mediaanalytics/main_mediaanalytics.cpp
@@ -33,6 +33,11 @@
{
signal(SIGPIPE, SIG_IGN);
+ // to match the service name
+ // we're replacing "/system/bin/mediaanalytics" with "media.analytics"
+ // we add a ".", but discard the path components: we finish with a shorter string
+ strcpy(argv[0], "media.analytics");
+
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm(defaultServiceManager());
ALOGI("ServiceManager: %p", sm.get());