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());