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

Commit 3938dc63 authored by Ray Essick's avatar Ray Essick
Browse files

initial mediasanalytics framework

This encompasses the basic framework files for mediaanalytics.  This
includes the library code to run in clients, the interface definitions
to get to the service, and the mediaanalytics service.

This version of the patchset also incorporates a wholesale change from
"Media Statistics" to "Media Analytics" -- which involved both textual
changes and file renaming

This does not include the changes to other entities that will be sending
data. We'll do those under separate per-entity commits.

Bug: 30267133
Test: run through cts' media stress tests
Change-Id: Iee7e722d10fd57c5d6b14e2947117ed0d3af4f2a
parent c2d29c48
Loading
Loading
Loading
Loading
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_IMEDIAANALYTICSSERVICE_H
#define ANDROID_IMEDIAANALYTICSSERVICE_H

#include <utils/String8.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>

#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <utils/List.h>

#include <binder/IServiceManager.h>

#include <media/MediaAnalyticsItem.h>
// nope...#include <media/MediaAnalytics.h>

namespace android {

class IMediaAnalyticsService: public IInterface
{
public:
    DECLARE_META_INTERFACE(MediaAnalyticsService);

    // generate a unique sessionID to use across multiple requests
    // 'unique' is within this device, since last reboot
    virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() = 0;

    // submit the indicated record to the mediaanalytics service, where
    // it will be merged (if appropriate) with incomplete records that
    // share the same key and sessionid.
    // '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;


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

};

// ----------------------------------------------------------------------------

class BnMediaAnalyticsService: public BnInterface<IMediaAnalyticsService>
{
public:
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);
};

}; // namespace android

#endif // ANDROID_IMEDIASTATISTICSSERVICE_H
+218 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_MEDIA_MEDIAANALYTICSITEM_H
#define ANDROID_MEDIA_MEDIAANALYTICSITEM_H

#include <cutils/properties.h>
#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>

#include <media/stagefright/foundation/AString.h>

namespace android {



class IMediaAnalyticsService;

// the class interface
//

class MediaAnalyticsItem : public RefBase {

    friend class MediaAnalyticsService;
    friend class IMediaAnalyticsService;

    public:

        // sessionid
        // unique within device, within boot,
        typedef int64_t SessionID_t;
        static constexpr SessionID_t SessionIDInvalid = -1;
        static constexpr SessionID_t SessionIDNone = 0;

        // Key: the record descriminator
        // values for the record discriminator
        // values can be "component/component"
        // basic values: "video", "audio", "drm"
        // XXX: need to better define the format
        typedef AString Key;
        static const Key kKeyNone;              // ""
        static const Key kKeyAny;               // "*"

        // Attr: names for attributes within a record
        // format "prop1" or "prop/subprop"
        // XXX: need to better define the format
        typedef AString Attr;


    public:

        // access functions for the class
        MediaAnalyticsItem();
        MediaAnalyticsItem(Key);
        ~MediaAnalyticsItem();

        // so clients can send intermediate values to be overlaid later
        MediaAnalyticsItem &setFinalized(bool);
        bool getFinalized() const;

        // SessionID ties multiple submissions for same key together
        // so that if video "height" and "width" are known at one point
        // and "framerate" is only known later, they can be be brought
        // together.
        MediaAnalyticsItem &setSessionID(SessionID_t);
        MediaAnalyticsItem &clearSessionID();
        SessionID_t getSessionID() const;
        // generates and stores a new ID iff mSessionID == SessionIDNone
        SessionID_t generateSessionID();

        // reset all contents, discarding any extra data
        void clear();

        // set the key discriminator for the record.
        // most often initialized as part of the constructor
        MediaAnalyticsItem &setKey(MediaAnalyticsItem::Key);
        MediaAnalyticsItem::Key getKey();

        // # of attributes in the record
        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);

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

        // find & extract values
        // return indicates whether attr exists (and thus value filled in)
        bool getInt32(Attr, int32_t *value);
        bool getInt64(Attr, int64_t *value);
        bool getDouble(Attr, double *value);
        bool getCString(Attr, char **value);

        // parameter indicates whether to close any existing open
        // record with same key before establishing a new record
        bool selfrecord(bool);
        bool selfrecord();

        // remove indicated attributes and their values
        // filterNot() could also be called keepOnly()
        // return value is # attributes removed
        // XXX: perhaps 'remove' instead of 'filter'
        // XXX: filterNot would become 'keep'
        int32_t filter(int count, Attr attrs[]);
        int32_t filterNot(int count, Attr attrs[]);
        int32_t filter(Attr attr);

        // below here are used on server side or to talk to server
        // clients need not worry about these.

        // timestamp, pid, and uid only used on server side
	// timestamp is in 'nanoseconds, unix time'
        MediaAnalyticsItem &setTimestamp(nsecs_t);
        nsecs_t getTimestamp() const;

        MediaAnalyticsItem &setPid(pid_t);
        pid_t getPid() const;

        MediaAnalyticsItem &setUid(uid_t);
        uid_t getUid() const;

        // our serialization code for binder calls
        int32_t writeToParcel(Parcel *);
        int32_t readFromParcel(const Parcel&);

        AString toString();

        // are we collecting analytics data
        static bool isEnabled();

    protected:

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

        // enabled 1, disabled 0
        static const char * const EnabledProperty;
        static const char * const EnabledPropertyPersist;
        static const int   EnabledProperty_default;

    private:

        // to help validate that A doesn't mess with B's records
        pid_t     mPid;
        uid_t     mUid;

        // let's reuse a binder connection
        static sp<IMediaAnalyticsService> sAnalyticsService;
        static sp<IMediaAnalyticsService> getInstance();

        // tracking information
        SessionID_t mSessionID;         // grouping similar records
        nsecs_t mTimestamp;             // ns, system_time_monotonic

        // will this record accept further updates
        bool mFinalized;

        Key mKey;

        class Item : public RefBase {

         public:

            enum Type {
                kTypeNone = 0,
                kTypeInt32 = 1,
                kTypeInt64 = 2,
                kTypeDouble = 3,
                kTypeCString = 4,
            };

            Item();
            ~Item();
            void clear();

            Type mType;
            union {
                    int32_t int32Value;
                    int64_t int64Value;
                    double doubleValue;
                    char *CStringValue;
            } u;
        };
        KeyedVector<Attr, sp<Item>> mItems;

};

} // namespace android

#endif
+2 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ LOCAL_SRC_FILES += \
    IMediaPlayer.cpp \
    IMediaRecorder.cpp \
    IMediaSource.cpp \
    IMediaAnalyticsService.cpp \
    IRemoteDisplay.cpp \
    IRemoteDisplayClient.cpp \
    IResourceManagerClient.cpp \
@@ -34,6 +35,7 @@ LOCAL_SRC_FILES += \
    MediaCodecBuffer.cpp \
    MediaCodecInfo.cpp \
    MediaDefs.cpp \
    MediaAnalyticsItem.cpp \
    MediaUtils.cpp \
    Metadata.cpp \
    mediarecorder.cpp \
+241 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "MediaAnalytics"

#include <stdint.h>
#include <inttypes.h>
#include <sys/types.h>

#include <binder/Parcel.h>
#include <binder/IMemory.h>
#include <binder/IPCThreadState.h>
#include <media/IHDCP.h>
#include <media/IMediaCodecList.h>
#include <media/IMediaHTTPService.h>
#include <media/IMediaPlayerService.h>
#include <media/IMediaRecorder.h>
#include <media/IOMX.h>
#include <media/IRemoteDisplay.h>
#include <media/IRemoteDisplayClient.h>
#include <media/IStreamSource.h>

#include <utils/Errors.h>  // for status_t
#include <utils/List.h>
#include <utils/Log.h>
#include <utils/String8.h>

#include <media/MediaAnalyticsItem.h>
#include <media/IMediaAnalyticsService.h>

#define DEBUGGING               0
#define DEBUGGING_FLOW          0
#define DEBUGGING_RETURNS       0

namespace android {

enum {
    GENERATE_UNIQUE_SESSIONID = IBinder::FIRST_CALL_TRANSACTION,
    SUBMIT_ITEM,
    GET_ITEM_LIST,
};

class BpMediaAnalyticsService: public BpInterface<IMediaAnalyticsService>
{
public:
    explicit BpMediaAnalyticsService(const sp<IBinder>& impl)
        : BpInterface<IMediaAnalyticsService>(impl)
    {
    }

    virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() {
        Parcel data, reply;
        status_t err;
        MediaAnalyticsItem::SessionID_t sessionid =
                        MediaAnalyticsItem::SessionIDInvalid;

        data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
        err = remote()->transact(GENERATE_UNIQUE_SESSIONID, data, &reply);
        if (err != NO_ERROR) {
            ALOGW("bad response from service");
            return MediaAnalyticsItem::SessionIDInvalid;
        }
        sessionid = reply.readInt64();
        if (DEBUGGING_RETURNS) {
            ALOGD("the caller gets a sessionid of %" PRId64 " back", sessionid);
        }
        return sessionid;
    }

    virtual MediaAnalyticsItem::SessionID_t submit(sp<MediaAnalyticsItem> item, bool forcenew)
    {
        // have this record submit itself
        // this will be a binder call with appropriate timing
        // return value is the uuid that the system generated for it.
        // the return value 0 and -1 are reserved.
        // -1 to indicate that there was a problem recording...

        Parcel data, reply;
        status_t err;

        if (item == NULL) {
                return MediaAnalyticsItem::SessionIDInvalid;
        }

        data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
        if(DEBUGGING_FLOW) {
            ALOGD("client offers record: %s", item->toString().c_str());
        }
        data.writeBool(forcenew);
        item->writeToParcel(&data);

        err = remote()->transact(SUBMIT_ITEM, data, &reply);
        if (err != NO_ERROR) {
            return MediaAnalyticsItem::SessionIDInvalid;
        }

        // get an answer out of 'reply'
        int64_t sessionid = reply.readInt64();
        if (DEBUGGING_RETURNS) {
            ALOGD("the caller gets sessionid=%" PRId64 "", sessionid);
        }
        return sessionid;
    }

    virtual List<sp<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)
    {
        Parcel data, reply;
        status_t err;

        data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
        data.writeInt32(finished);
        data.writeInt64(ts);
        const char *str = key.c_str();
        if (key.empty()) {
            str = MediaAnalyticsItem::kKeyNone.c_str();
        }
        data.writeCString(str);
        err = remote()->transact(GET_ITEM_LIST, data, &reply);
	if (err != NO_ERROR) {
	    return NULL;
	}

        // read a count
        int32_t count = reply.readInt32();
        List<sp<MediaAnalyticsItem>> *list = NULL;

        if (count > 0) {
            list = new List<sp<MediaAnalyticsItem>>();
            for (int i=0;i<count;i++) {
                sp<MediaAnalyticsItem> item = new MediaAnalyticsItem;
                // XXX: watch for failures here
                item->readFromParcel(reply);
                list->push_back(item);
            }
        }

        return list;
    }
};

IMPLEMENT_META_INTERFACE(MediaAnalyticsService, "android.media.IMediaAnalyticsService");

// ----------------------------------------------------------------------

status_t BnMediaAnalyticsService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{


    // get calling pid/tid
    IPCThreadState *ipc = IPCThreadState::self();
    int clientPid = ipc->getCallingPid();
    // permission checking

    if(DEBUGGING_FLOW) {
        ALOGD("running in service, code %d, pid %d; called from pid %d",
            code, getpid(), clientPid);
    }

    switch (code) {

        case GENERATE_UNIQUE_SESSIONID: {
            CHECK_INTERFACE(IMediaAnalyticsService, data, reply);

            MediaAnalyticsItem::SessionID_t sessionid = generateUniqueSessionID();
            reply->writeInt64(sessionid);

            return NO_ERROR;
        } break;

        case SUBMIT_ITEM: {
            CHECK_INTERFACE(IMediaAnalyticsService, data, reply);

            bool forcenew;
            sp<MediaAnalyticsItem> item = new MediaAnalyticsItem;

            data.readBool(&forcenew);
            item->readFromParcel(data);

            item->setPid(clientPid);

	    // submit() takes ownership of / responsibility for the item
            MediaAnalyticsItem::SessionID_t sessionid = submit(item, forcenew);
            reply->writeInt64(sessionid);

            return NO_ERROR;
        } break;

        case GET_ITEM_LIST: {
            CHECK_INTERFACE(IMediaPlayerService, data, reply);
            // get the parameters
            bool finished = data.readInt32();
            nsecs_t ts = data.readInt64();
            MediaAnalyticsItem::Key key = data.readCString();

            // find the (0 or more) items
            List<sp<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();
                         it != list->end(); it++) {
                            (*it)->writeToParcel(reply);
                    }


            }

            // avoid leakiness; organized discarding of list and its contents
            list->clear();
            delete list;

            return NO_ERROR;
        } break;

        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

// ----------------------------------------------------------------------------

} // namespace android
+703 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading