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

Commit ae3024f8 authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge changes Ieb901076,Idce65fd2

* changes:
  MediaMetrics: Add const correctness for items in service
  MediaMetrics: Add stack allocated Item
parents 068d0e3c 82a074b1
Loading
Loading
Loading
Loading
+45 −8
Original line number Diff line number Diff line
@@ -34,8 +34,11 @@

namespace android {

// TODO: Currently ONE_WAY transactions, make both ONE_WAY and synchronous options.

enum {
    SUBMIT_ITEM_ONEWAY = IBinder::FIRST_CALL_TRANSACTION,
    SUBMIT_ITEM = IBinder::FIRST_CALL_TRANSACTION,
    SUBMIT_BUFFER,
};

class BpMediaAnalyticsService: public BpInterface<IMediaAnalyticsService>
@@ -62,7 +65,30 @@ public:
        }

        status = remote()->transact(
                SUBMIT_ITEM_ONEWAY, data, nullptr /* reply */, IBinder::FLAG_ONEWAY);
                SUBMIT_ITEM, data, nullptr /* reply */, IBinder::FLAG_ONEWAY);
        ALOGW_IF(status != NO_ERROR, "%s: bad response from service for submit, status=%d",
                __func__, status);
        return status;
    }

    status_t submitBuffer(const char *buffer, size_t length) override
    {
        if (buffer == nullptr || length > INT32_MAX) {
            return BAD_VALUE;
        }
        ALOGV("%s: (ONEWAY) length:%zu", __func__, length);

        Parcel data;
        data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());

        status_t status = data.writeInt32(length)
                ?: data.write((uint8_t*)buffer, length);
        if (status != NO_ERROR) {
            return status;
        }

        status = remote()->transact(
                SUBMIT_BUFFER, data, nullptr /* reply */, IBinder::FLAG_ONEWAY);
        ALOGW_IF(status != NO_ERROR, "%s: bad response from service for submit, status=%d",
                __func__, status);
        return status;
@@ -76,10 +102,8 @@ IMPLEMENT_META_INTERFACE(MediaAnalyticsService, "android.media.IMediaAnalyticsSe
status_t BnMediaAnalyticsService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    const int clientPid = IPCThreadState::self()->getCallingPid();

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

        MediaAnalyticsItem * const item = MediaAnalyticsItem::create();
@@ -87,12 +111,25 @@ status_t BnMediaAnalyticsService::onTransact(
        if (status != NO_ERROR) { // assume failure logged in item
            return status;
        }
        // TODO: remove this setPid.
        item->setPid(clientPid);
        status = submitInternal(item, true /* release */);
        // assume failure logged by submitInternal
        return NO_ERROR;
    } break;
    }
    case SUBMIT_BUFFER: {
        CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
        int32_t length;
        status_t status = data.readInt32(&length);
        if (status != NO_ERROR || length <= 0) {
            return BAD_VALUE;
        }
        const void *ptr = data.readInplace(length);
        if (ptr == nullptr) {
            return BAD_VALUE;
        }
        status = submitBuffer(static_cast<const char *>(ptr), length);
        // assume failure logged by submitBuffer
        return NO_ERROR;
    }

    default:
        return BBinder::onTransact(code, data, reply, flags);
+111 −118
Original line number Diff line number Diff line
@@ -35,6 +35,10 @@
#include <media/MediaAnalyticsItem.h>
#include <private/android_filesystem_config.h>

// Max per-property string size before truncation in toString().
// Do not make too large, as this is used for dumpsys purposes.
static constexpr size_t kMaxPropertyStringSize = 4096;

namespace android {

#define DEBUG_SERVICEACCESS     0
@@ -367,70 +371,18 @@ std::string MediaAnalyticsItem::toString() const {
}

std::string MediaAnalyticsItem::toString(int version) const {

    // v0 : released with 'o'
    // v1 : bug fix (missing pid/finalized separator),
    //      adds apk name, apk version code

    if (version <= PROTO_FIRST) {
        // default to original v0 format, until proper parsers are in place
        version = PROTO_V0;
    } else if (version > PROTO_LAST) {
        version = PROTO_LAST;
    }

    std::string result;
    char buffer[512];

    if (version == PROTO_V0) {
        result = "(";
    } else {
        snprintf(buffer, sizeof(buffer), "[%d:", version);
        result.append(buffer);
    }

    // same order as we spill into the parcel, although not required
    // key+session are our primary matching criteria
    result.append(mKey.c_str());
    result.append(":0:"); // sessionID

    snprintf(buffer, sizeof(buffer), "%d:", mUid);
    result.append(buffer);

    if (version >= PROTO_V1) {
        result.append(mPkgName);
        snprintf(buffer, sizeof(buffer), ":%"  PRId64 ":", mPkgVersionCode);
        result.append(buffer);
    }
    char buffer[kMaxPropertyStringSize];

    // in 'o' (v1) , the separator between pid and finalized was omitted
    if (version <= PROTO_V0) {
        snprintf(buffer, sizeof(buffer), "%d", mPid);
    } else {
        snprintf(buffer, sizeof(buffer), "%d:", mPid);
    }
    result.append(buffer);

    snprintf(buffer, sizeof(buffer), "%d:", 0 /* finalized */); // TODO: remove this.
    result.append(buffer);
    snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mTimestamp);
    snprintf(buffer, sizeof(buffer), "[%d:%s:%d:%d:%lld:%s:%zu:",
            version, mKey.c_str(), mPid, mUid, (long long)mTimestamp,
            mPkgName.c_str(), mPropCount);
    result.append(buffer);

    // set of items
    int count = mPropCount;
    snprintf(buffer, sizeof(buffer), "%d:", count);
    result.append(buffer);
    for (int i = 0 ; i < count; i++ ) {
    for (size_t i = 0 ; i < mPropCount; ++i) {
        mProps[i].toString(buffer, sizeof(buffer));
        result.append(buffer);
    }

    if (version == PROTO_V0) {
        result.append(")");
    } else {
    result.append("]");
    }

    return result;
}

@@ -451,8 +403,9 @@ bool MediaAnalyticsItem::selfrecord() {
    }
}

namespace mediametrics {
//static
bool MediaAnalyticsItem::isEnabled() {
bool BaseItem::isEnabled() {
    // completely skip logging from certain UIDs. We do this here
    // to avoid the multi-second timeouts while we learn that
    // sepolicy will not let us find the service.
@@ -481,25 +434,47 @@ bool MediaAnalyticsItem::isEnabled() {
class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
        virtual void binderDied(const wp<IBinder> &) {
            ALOGW("Reacquire service connection on next request");
            MediaAnalyticsItem::dropInstance();
            BaseItem::dropInstance();
        }
};

static sp<MediaMetricsDeathNotifier> sNotifier;
// static
sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
sp<IMediaAnalyticsService> BaseItem::sAnalyticsService;
static std::mutex sServiceMutex;
static int sRemainingBindAttempts = SVC_TRIES;

// static
void MediaAnalyticsItem::dropInstance() {
void BaseItem::dropInstance() {
    std::lock_guard  _l(sServiceMutex);
    sRemainingBindAttempts = SVC_TRIES;
    sAnalyticsService = nullptr;
}

// static
sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
bool BaseItem::submitBuffer(const char *buffer, size_t size) {
/*
    MediaAnalyticsItem item;
    status_t status = item.readFromByteString(buffer, size);
    ALOGD("%s: status:%d, size:%zu, item:%s", __func__, status, size, item.toString().c_str());
    return item.selfrecord();
    */

    ALOGD_IF(DEBUG_API, "%s: delivering %zu bytes", __func__, size);
    sp<IMediaAnalyticsService> svc = getInstance();
    if (svc != nullptr) {
        const status_t status = svc->submitBuffer(buffer, size);
        if (status != NO_ERROR) {
            ALOGW("%s: failed(%d) to record: %zu bytes", __func__, status, size);
            return false;
        }
        return true;
    }
    return false;
}

//static
sp<IMediaAnalyticsService> BaseItem::getInstance() {
    static const char *servicename = "media.metrics";
    static const bool enabled = isEnabled(); // singleton initialized

@@ -537,6 +512,8 @@ sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
    return sAnalyticsService;
}

} // namespace mediametrics

// merge the info from 'incoming' into this record.
// we finish with a union of this+incoming and special handling for collisions
bool MediaAnalyticsItem::merge(MediaAnalyticsItem *incoming) {
@@ -633,6 +610,7 @@ status_t extract(char **val, const char **bufferpptr, const char *bufferptrmax)
    while (*ptr != 0) {
        if (ptr >= bufferptrmax) {
            ALOGE("%s: buffer exceeded", __func__);
            return BAD_VALUE;
        }
        ++ptr;
    }
@@ -657,39 +635,41 @@ status_t MediaAnalyticsItem::writeToByteString(char **pbuffer, size_t *plength)
        return INVALID_OPERATION;
    }
    const uint16_t version = 0;
    const uint32_t header_len =
        sizeof(uint32_t)     // overall length
        + sizeof(header_len) // header length
    const uint32_t header_size =
        sizeof(uint32_t)      // total size
        + sizeof(header_size) // header size
        + sizeof(version)     // encoding version
        + sizeof(uint16_t)   // key length
        + sizeof(uint16_t)    // key size
        + keySizeZeroTerminated // key, zero terminated
        + sizeof(int32_t)     // pid
        + sizeof(int32_t)     // uid
        + sizeof(int64_t)     // timestamp
        ;

    uint32_t len = header_len
    uint32_t size = header_size
        + sizeof(uint32_t) // # properties
        ;
    for (size_t i = 0 ; i < mPropCount; ++i) {
        const size_t size = mProps[i].getByteStringSize();
        if (size > UINT_MAX - 1) {
            ALOGW("%s: prop %zu has size %zu", __func__, i, size);
        const size_t propSize = mProps[i].getByteStringSize();
        if (propSize > UINT16_MAX) {
            ALOGW("%s: prop %zu size %zu too large", __func__, i, propSize);
            return INVALID_OPERATION;
        }
        if (__builtin_add_overflow(size, propSize, &size)) {
            ALOGW("%s: item size overflow at property %zu", __func__, i);
            return INVALID_OPERATION;
        }
        len += size;
    }

    // TODO: consider package information and timestamp.

    // now that we have a size... let's allocate and fill
    char *build = (char *)calloc(1 /* nmemb */, len);
    // since we fill every byte in the buffer (there is no padding),
    // malloc is used here instead of calloc.
    char * const build = (char *)malloc(size);
    if (build == nullptr) return NO_MEMORY;

    char *filling = build;
    char *buildmax = build + len;
    if (insert(len, &filling, buildmax) != NO_ERROR
            || insert(header_len, &filling, buildmax) != NO_ERROR
    char *buildmax = build + size;
    if (insert((uint32_t)size, &filling, buildmax) != NO_ERROR
            || insert(header_size, &filling, buildmax) != NO_ERROR
            || insert(version, &filling, buildmax) != NO_ERROR
            || insert((uint16_t)keySizeZeroTerminated, &filling, buildmax) != NO_ERROR
            || insert(mKey.c_str(), &filling, buildmax) != NO_ERROR
@@ -697,26 +677,27 @@ status_t MediaAnalyticsItem::writeToByteString(char **pbuffer, size_t *plength)
            || insert((int32_t)mUid, &filling, buildmax) != NO_ERROR
            || insert((int64_t)mTimestamp, &filling, buildmax) != NO_ERROR
            || insert((uint32_t)mPropCount, &filling, buildmax) != NO_ERROR) {
        ALOGD("%s:could not write header", __func__);
        ALOGE("%s:could not write header", __func__);  // shouldn't happen
        free(build);
        return INVALID_OPERATION;
    }
    for (size_t i = 0 ; i < mPropCount; ++i) {
        if (mProps[i].writeToByteString(&filling, buildmax) != NO_ERROR) {
            free(build);
            ALOGD("%s:could not write prop %zu of %zu", __func__, i, mPropCount);
            // shouldn't happen
            ALOGE("%s:could not write prop %zu of %zu", __func__, i, mPropCount);
            return INVALID_OPERATION;
        }
    }

    if (filling != buildmax) {
        ALOGE("problems populating; wrote=%d planned=%d",
              (int)(filling - build), len);
        ALOGE("%s: problems populating; wrote=%d planned=%d",
                __func__, (int)(filling - build), (int)size);
        free(build);
        return INVALID_OPERATION;
    }
    *pbuffer = build;
    *plength = len;
    *plength = size;
    return NO_ERROR;
}

@@ -727,40 +708,41 @@ status_t MediaAnalyticsItem::readFromByteString(const char *bufferptr, size_t le
    const char *read = bufferptr;
    const char *readend = bufferptr + length;

    uint32_t len;
    uint32_t header_len;
    int16_t version;
    int16_t key_len;
    uint32_t size;
    uint32_t header_size;
    uint16_t version;
    uint16_t key_size;
    char *key = nullptr;
    int32_t pid;
    int32_t uid;
    int64_t timestamp;
    uint32_t propCount;
    if (extract(&len, &read, readend) != NO_ERROR
            || extract(&header_len, &read, readend) != NO_ERROR
    if (extract(&size, &read, readend) != NO_ERROR
            || extract(&header_size, &read, readend) != NO_ERROR
            || extract(&version, &read, readend) != NO_ERROR
            || extract(&key_len, &read, readend) != NO_ERROR
            || extract(&key_size, &read, readend) != NO_ERROR
            || extract(&key, &read, readend) != NO_ERROR
            || extract(&pid, &read, readend) != NO_ERROR
            || extract(&uid, &read, readend) != NO_ERROR
            || extract(&timestamp, &read, readend) != NO_ERROR
            || len > length
            || header_len > len) {
            || size > length
            || strlen(key) + 1 != key_size
            || header_size > size) {
        free(key);
        ALOGD("%s: invalid header", __func__);
        ALOGW("%s: invalid header", __func__);
        return INVALID_OPERATION;
    }
    mKey = key;
    free(key);
    const size_t pos = read - bufferptr;
    if (pos > header_len) {
        ALOGD("%s: invalid header pos:%zu > header_len:%u",
                __func__, pos, header_len);
    if (pos > header_size) {
        ALOGW("%s: invalid header pos:%zu > header_size:%u",
                __func__, pos, header_size);
        return INVALID_OPERATION;
    } else if (pos < header_len) {
        ALOGD("%s: mismatched header pos:%zu < header_len:%u, advancing",
                __func__, pos, header_len);
        read += (header_len - pos);
    } else if (pos < header_size) {
        ALOGW("%s: mismatched header pos:%zu < header_size:%u, advancing",
                __func__, pos, header_size);
        read += (header_size - pos);
    }
    if (extract(&propCount, &read, readend) != NO_ERROR) {
        ALOGD("%s: cannot read prop count", __func__);
@@ -772,7 +754,7 @@ status_t MediaAnalyticsItem::readFromByteString(const char *bufferptr, size_t le
    for (size_t i = 0; i < propCount; ++i) {
        Prop *prop = allocateProp();
        if (prop->readFromByteString(&read, readend) != NO_ERROR) {
            ALOGD("%s: cannot read prop %zu", __func__, i);
            ALOGW("%s: cannot read prop %zu", __func__, i);
            return INVALID_OPERATION;
        }
    }
@@ -910,8 +892,10 @@ size_t MediaAnalyticsItem::Prop::getByteStringSize() const
    return header + payload;
}

namespace mediametrics {

// TODO: fold into a template later.
status_t MediaAnalyticsItem::writeToByteString(
status_t BaseItem::writeToByteString(
        const char *name, int32_t value, char **bufferpptr, char *bufferptrmax)
{
    const size_t len = 2 + 1 + strlen(name) + 1 + sizeof(value);
@@ -922,7 +906,7 @@ status_t MediaAnalyticsItem::writeToByteString(
            ?: insert(value, bufferpptr, bufferptrmax);
}

status_t MediaAnalyticsItem::writeToByteString(
status_t BaseItem::writeToByteString(
        const char *name, int64_t value, char **bufferpptr, char *bufferptrmax)
{
    const size_t len = 2 + 1 + strlen(name) + 1 + sizeof(value);
@@ -933,7 +917,7 @@ status_t MediaAnalyticsItem::writeToByteString(
            ?: insert(value, bufferpptr, bufferptrmax);
}

status_t MediaAnalyticsItem::writeToByteString(
status_t BaseItem::writeToByteString(
        const char *name, double value, char **bufferpptr, char *bufferptrmax)
{
    const size_t len = 2 + 1 + strlen(name) + 1 + sizeof(value);
@@ -944,7 +928,7 @@ status_t MediaAnalyticsItem::writeToByteString(
            ?: insert(value, bufferpptr, bufferptrmax);
}

status_t MediaAnalyticsItem::writeToByteString(
status_t BaseItem::writeToByteString(
        const char *name, const std::pair<int64_t, int64_t> &value, char **bufferpptr, char *bufferptrmax)
{
    const size_t len = 2 + 1 + strlen(name) + 1 + 8 + 8;
@@ -956,8 +940,14 @@ status_t MediaAnalyticsItem::writeToByteString(
            ?: insert(value.second, bufferpptr, bufferptrmax);
}

status_t MediaAnalyticsItem::writeToByteString(
status_t BaseItem::writeToByteString(
        const char *name, char * const &value, char **bufferpptr, char *bufferptrmax)
{
    return writeToByteString(name, (const char *)value, bufferpptr, bufferptrmax);
}

status_t BaseItem::writeToByteString(
        const char *name, const char * const &value, char **bufferpptr, char *bufferptrmax)
{
    const size_t len = 2 + 1 + strlen(name) + 1 + strlen(value) + 1;
    if (len > UINT16_MAX) return BAD_VALUE;
@@ -967,7 +957,8 @@ status_t MediaAnalyticsItem::writeToByteString(
            ?: insert(value, bufferpptr, bufferptrmax);
}

status_t MediaAnalyticsItem::writeToByteString(

status_t BaseItem::writeToByteString(
        const char *name, const none_t &, char **bufferpptr, char *bufferptrmax)
{
    const size_t len = 2 + 1 + strlen(name) + 1;
@@ -977,22 +968,24 @@ status_t MediaAnalyticsItem::writeToByteString(
            ?: insert(name, bufferpptr, bufferptrmax);
}

} // namespace mediametrics

status_t MediaAnalyticsItem::Prop::writeToByteString(
        char **bufferpptr, char *bufferptrmax) const
{
    switch (mType) {
    case kTypeInt32:
        return MediaAnalyticsItem::writeToByteString(mName, u.int32Value, bufferpptr, bufferptrmax);
        return BaseItem::writeToByteString(mName, u.int32Value, bufferpptr, bufferptrmax);
    case kTypeInt64:
        return MediaAnalyticsItem::writeToByteString(mName, u.int64Value, bufferpptr, bufferptrmax);
        return BaseItem::writeToByteString(mName, u.int64Value, bufferpptr, bufferptrmax);
    case kTypeDouble:
        return MediaAnalyticsItem::writeToByteString(mName, u.doubleValue, bufferpptr, bufferptrmax);
        return BaseItem::writeToByteString(mName, u.doubleValue, bufferpptr, bufferptrmax);
    case kTypeRate:
        return MediaAnalyticsItem::writeToByteString(mName, u.rate, bufferpptr, bufferptrmax);
        return BaseItem::writeToByteString(mName, u.rate, bufferpptr, bufferptrmax);
    case kTypeCString:
        return MediaAnalyticsItem::writeToByteString(mName, u.CStringValue, bufferpptr, bufferptrmax);
        return BaseItem::writeToByteString(mName, u.CStringValue, bufferpptr, bufferptrmax);
    case kTypeNone:
        return MediaAnalyticsItem::writeToByteString(mName, none_t{}, bufferpptr, bufferptrmax);
        return BaseItem::writeToByteString(mName, none_t{}, bufferpptr, bufferptrmax);
    default:
        ALOGE("%s: found bad prop type: %d, name %s",
                __func__, mType, mName);  // no payload sent
+2 −0
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ public:
               may be silent and return 0 - success).
     */
    virtual status_t submit(MediaAnalyticsItem *item) = 0;

    virtual status_t submitBuffer(const char *buffer, size_t length) = 0;
};

// ----------------------------------------------------------------------------
+340 −46

File changed.

Preview size limit exceeded, changes collapsed.

+10 −8
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@

namespace android {

using namespace mediametrics;

// individual records kept in memory: age or count
// age: <= 28 hours (1 1/6 days)
// count: hard limit of # records
@@ -138,12 +140,12 @@ status_t MediaAnalyticsService::submitInternal(MediaAnalyticsItem *item, bool re
        return BAD_VALUE;
    }

    // send to statsd
    extern bool dump2Statsd(MediaAnalyticsItem *item);  // extern hook
    (void)dump2Statsd(item);  // failure should be logged in function.
    // now attach either the item or its dup to a const shared pointer
    std::shared_ptr<const MediaAnalyticsItem> sitem(release ? item : item->dup());

    if (!release) item = item->dup();
    saveItem(item);
    extern bool dump2Statsd(const std::shared_ptr<const MediaAnalyticsItem>& item);
    (void)dump2Statsd(sitem);  // failure should be logged in function.
    saveItem(sitem);
    return NO_ERROR;
}

@@ -323,7 +325,7 @@ void MediaAnalyticsService::dumpQueue_l(

// if item != NULL, it's the item we just inserted
// true == more items eligible to be recovered
bool MediaAnalyticsService::expirations_l(MediaAnalyticsItem *item)
bool MediaAnalyticsService::expirations_l(const std::shared_ptr<const MediaAnalyticsItem>& item)
{
    bool more = false;

@@ -346,7 +348,7 @@ bool MediaAnalyticsService::expirations_l(MediaAnalyticsItem *item)
        for (; i < mItems.size(); ++i) {
            auto &oitem = mItems[i];
            nsecs_t when = oitem->getTimestamp();
            if (oitem.get() == item) {
            if (oitem.get() == item.get()) {
                break;
            }
            if (now > when && (now - when) <= mMaxRecordAgeNs) {
@@ -382,7 +384,7 @@ void MediaAnalyticsService::processExpirations()
    } while (more);
}

void MediaAnalyticsService::saveItem(MediaAnalyticsItem *item)
void MediaAnalyticsService::saveItem(const std::shared_ptr<const MediaAnalyticsItem>& item)
{
    std::lock_guard _l(mLock);
    // we assume the items are roughly in time order.
Loading