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/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);
 
 };