Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b5fac8ef authored by Ray Essick's avatar Ray Essick
Browse files

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
parent 51ac3550
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -49,14 +49,16 @@ public:
    // '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;

};

+49 −29
Original line number Diff line number Diff line
@@ -36,13 +36,22 @@ class IMediaAnalyticsService;
// 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 @@ class MediaAnalyticsItem : public RefBase {
        // 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 @@ class MediaAnalyticsItem : public RefBase {

        // 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 @@ class MediaAnalyticsItem : public RefBase {
        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();

@@ -159,7 +173,8 @@ class MediaAnalyticsItem : public RefBase {
        // 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 @@ class MediaAnalyticsItem : public RefBase {

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

        void initProp(Prop *item);
        void clearProp(Prop *item);
        void clearPropValue(Prop *item);
        void copyProp(Prop *dst, const Prop *src);
        enum {
            kGrowProps = 10
        };
        KeyedVector<Attr, sp<Item>> mItems;
        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;

};

+13 −13
Original line number Diff line number Diff line
@@ -80,7 +80,7 @@ public:
        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 @@ public:
        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;
@@ -140,12 +140,12 @@ public:

        // 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 @@ status_t BnMediaAnalyticsService::onTransact(
            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 @@ status_t BnMediaAnalyticsService::onTransact(
            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);
                    }
+446 −284

File changed.

Preview size limit exceeded, changes collapsed.

+150 −80
Original line number Diff line number Diff line
@@ -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 @@ MediaAnalyticsService::MediaAnalyticsService()

    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 @@ MediaAnalyticsItem::SessionID_t MediaAnalyticsService::generateUniqueSessionID()
    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 @@ MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(sp<MediaAnalyticsI

    // validate the record; we discard if we don't like it
    if (contentValid(item) == false) {
        delete item;
        return MediaAnalyticsItem::SessionIDInvalid;
    }

@@ -155,13 +157,17 @@ MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(sp<MediaAnalyticsI
    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 @@ MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(sp<MediaAnalyticsI
                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
        id = item->getSessionID();
        if (finalizing) {
                if (item->count() != 0) {
            if (item->count() == 0) {
                // drop empty records
                delete item;
                item = NULL;
            } else {
                saveItem(mFinalized, item, 0);
                mItemsFinalized++;
            }
        } else {
            saveItem(mOpen, item, 1);
        }
            id = item->getSessionID();
    }

    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,10 +243,35 @@ status_t MediaAnalyticsService::dump(int fd, const Vector<String16>&)
                IPCThreadState::self()->getCallingPid(),
                IPCThreadState::self()->getCallingUid());
        result.append(buffer);
    } else {

        // crack parameters
        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);

@@ -258,28 +292,51 @@ status_t MediaAnalyticsService::dump(int fd, const Vector<String16>&)
        " 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));
    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));
    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 @@ String8 MediaAnalyticsService::dumpQueue(List<sp<MediaAnalyticsItem>> *theList)
    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 @@ String8 MediaAnalyticsService::dumpQueue(List<sp<MediaAnalyticsItem>> *theList)
// 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 @@ void MediaAnalyticsService::saveItem(List<sp<MediaAnalyticsItem>> *l, sp<MediaAn
        l->push_back(item);
    }

    if (trackqueue) {
    if (DEBUG_QUEUE) {
        String8 after = dumpQueue(l);
        ALOGD("Q after insert: %s", after.string());
    }
@@ -332,25 +395,28 @@ void MediaAnalyticsService::saveItem(List<sp<MediaAnalyticsItem>> *l, sp<MediaAn
    // 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());
        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 @@ static bool compatibleItems(sp<MediaAnalyticsItem> oitem, sp<MediaAnalyticsItem>
}

// 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 @@ sp<MediaAnalyticsItem> MediaAnalyticsService::findItem(List<sp<MediaAnalyticsIte


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

        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 @@ bool MediaAnalyticsService::contentValid(sp<MediaAnalyticsItem>) {
}

// are we rate limited, normally false
bool MediaAnalyticsService::rateLimited(sp<MediaAnalyticsItem>) {
bool MediaAnalyticsService::rateLimited(MediaAnalyticsItem *) {

    return false;
}
Loading