Merge changes I76cdcce4,Ide5e9218

* changes:
  MediaMetrics: Update to use fluent style recording
  MediaMetrics: Make submit one-way
gugelfrei
Andy Hung 5 years ago committed by Android (Google) Code Review
commit 58e944d98b

@ -59,7 +59,6 @@ void DrmManager::reportEngineMetrics(
IDrmEngine& engine = mPlugInManager.getPlugIn(plugInId);
std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("drmmanager"));
item->generateSessionID();
item->setUid(IPCThreadState::self()->getCallingUid());
item->setCString("function_name", func);
item->setCString("plugin_id", plugInId.getPathLeaf().getBasePath().c_str());

@ -1598,7 +1598,6 @@ void DrmHal::writeByteArray(Parcel &obj, hidl_vec<uint8_t> const &vec)
void DrmHal::reportFrameworkMetrics() const
{
std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("mediadrm"));
item->generateSessionID();
item->setPkgName(mMetrics.GetAppPackageName().c_str());
String8 vendor;
String8 description;

@ -35,8 +35,6 @@ status_t reportVendorMetrics(const std::string& metrics,
const String8& name,
const String8& appPackageName) {
std::unique_ptr<MediaAnalyticsItem> analyticsItem(MediaAnalyticsItem::create(name.c_str()));
analyticsItem->generateSessionID();
std::string app_package_name(appPackageName.c_str(), appPackageName.size());
analyticsItem->setPkgName(app_package_name);
if (metrics.size() > 0) {
@ -44,7 +42,7 @@ status_t reportVendorMetrics(const std::string& metrics,
}
if (!analyticsItem->selfrecord()) {
ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem->getSessionID());
ALOGE("%s: selfrecord() returned false", __func__);
}
return OK;

@ -32,15 +32,10 @@
#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,
SUBMIT_ITEM_ONEWAY = IBinder::FIRST_CALL_TRANSACTION,
};
class BpMediaAnalyticsService: public BpInterface<IMediaAnalyticsService>
@ -51,61 +46,23 @@ public:
{
}
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 for generateSessionId, err=%d", err);
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(MediaAnalyticsItem *item, bool forcenew)
status_t submit(MediaAnalyticsItem *item) override
{
// 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;
if (item == nullptr) {
return BAD_VALUE;
}
ALOGV("%s: (ONEWAY) item=%s", __func__, item->toString().c_str());
Parcel data;
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) {
ALOGW("bad response from service for submit, err=%d", err);
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;
status_t err = remote()->transact(
SUBMIT_ITEM_ONEWAY, data, nullptr /* reply */, IBinder::FLAG_ONEWAY);
ALOGW_IF(err != NO_ERROR, "%s: bad response from service for submit, err=%d",
__func__, err);
return err;
}
};
IMPLEMENT_META_INTERFACE(MediaAnalyticsService, "android.media.IMediaAnalyticsService");
@ -115,49 +72,23 @@ IMPLEMENT_META_INTERFACE(MediaAnalyticsService, "android.media.IMediaAnalyticsSe
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);
}
const int clientPid = IPCThreadState::self()->getCallingPid();
switch (code) {
case SUBMIT_ITEM_ONEWAY: {
CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
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;
MediaAnalyticsItem *item = MediaAnalyticsItem::create();
data.readBool(&forcenew);
item->readFromParcel(data);
item->setPid(clientPid);
// submit() takes over ownership of 'item'
MediaAnalyticsItem::SessionID_t sessionid = submit(item, forcenew);
reply->writeInt64(sessionid);
return NO_ERROR;
} break;
MediaAnalyticsItem * const item = MediaAnalyticsItem::create();
if (item->readFromParcel(data) < 0) {
return BAD_VALUE;
}
item->setPid(clientPid);
const status_t status __unused = submitInternal(item, true /* release */);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
default:
return BBinder::onTransact(code, data, reply, flags);
}
}

@ -22,10 +22,11 @@
#include <string.h>
#include <sys/types.h>
#include <mutex>
#include <binder/Parcel.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Mutex.h>
#include <utils/SortedVector.h>
#include <utils/threads.h>
@ -44,14 +45,6 @@ namespace android {
// the service is off.
#define SVC_TRIES 2
// the few universal keys we have
const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyAny = "any";
const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyNone = "none";
const char * const MediaAnalyticsItem::EnabledProperty = "media.metrics.enabled";
const char * const MediaAnalyticsItem::EnabledPropertyPersist = "persist.media.metrics.enabled";
const int MediaAnalyticsItem::EnabledProperty_default = 1;
// So caller doesn't need to know size of allocated space
MediaAnalyticsItem *MediaAnalyticsItem::create()
{
@ -74,34 +67,6 @@ mediametrics_handle_t MediaAnalyticsItem::convert(MediaAnalyticsItem *item ) {
return handle;
}
// access functions for the class
MediaAnalyticsItem::MediaAnalyticsItem()
: mPid(-1),
mUid(-1),
mPkgVersionCode(0),
mSessionID(MediaAnalyticsItem::SessionIDNone),
mTimestamp(0),
mFinalized(1),
mPropCount(0), mPropSize(0), mProps(NULL)
{
mKey = MediaAnalyticsItem::kKeyNone;
}
MediaAnalyticsItem::MediaAnalyticsItem(MediaAnalyticsItem::Key key)
: mPid(-1),
mUid(-1),
mPkgVersionCode(0),
mSessionID(MediaAnalyticsItem::SessionIDNone),
mTimestamp(0),
mFinalized(1),
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);
@ -114,13 +79,10 @@ void MediaAnalyticsItem::clear() {
// clean allocated storage from key
mKey.clear();
// clean various major parameters
mSessionID = MediaAnalyticsItem::SessionIDNone;
// clean attributes
// contents of the attributes
for (size_t i = 0 ; i < mPropCount; i++ ) {
clearProp(&mProps[i]);
mProps[i].clear();
}
// the attribute records themselves
if (mProps != NULL) {
@ -143,14 +105,12 @@ MediaAnalyticsItem *MediaAnalyticsItem::dup() {
dst->mUid = this->mUid;
dst->mPkgName = this->mPkgName;
dst->mPkgVersionCode = this->mPkgVersionCode;
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->mProps[i] = this->mProps[i];
}
dst->mPropCount = this->mPropCount;
}
@ -158,35 +118,6 @@ MediaAnalyticsItem *MediaAnalyticsItem::dup() {
return dst;
}
MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
mSessionID = id;
return *this;
}
MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const {
return mSessionID;
}
MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::generateSessionID() {
if (mSessionID == SessionIDNone) {
// get one from the server
MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
sp<IMediaAnalyticsService> svc = getInstance();
if (svc != NULL) {
newid = svc->generateUniqueSessionID();
}
mSessionID = newid;
}
return mSessionID;
}
MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() {
mSessionID = MediaAnalyticsItem::SessionIDNone;
return *this;
}
MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) {
mTimestamp = ts;
return *this;
@ -240,22 +171,16 @@ int32_t MediaAnalyticsItem::count() const {
}
// find the proper entry in the list
size_t MediaAnalyticsItem::findPropIndex(const char *name, size_t len)
size_t MediaAnalyticsItem::findPropIndex(const char *name, size_t len) const
{
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;
}
if (mProps[i].isNamed(name, len)) break;
}
return i;
}
MediaAnalyticsItem::Prop *MediaAnalyticsItem::findProp(const char *name) {
MediaAnalyticsItem::Prop *MediaAnalyticsItem::findProp(const char *name) const {
size_t len = strlen(name);
size_t i = findPropIndex(name, len);
if (i < mPropCount) {
@ -264,16 +189,6 @@ MediaAnalyticsItem::Prop *MediaAnalyticsItem::findProp(const char *name) {
return NULL;
}
void MediaAnalyticsItem::Prop::setName(const char *name, size_t len) {
free((void *)mName);
mName = (const char *) malloc(len+1);
LOG_ALWAYS_FATAL_IF(mName == NULL,
"failed malloc() for property '%s' (len %zu)",
name, len);
memcpy ((void *)mName, name, len+1);
mNameLen = len;
}
// consider this "find-or-allocate".
// caller validates type and uses clearPropValue() accordingly
MediaAnalyticsItem::Prop *MediaAnalyticsItem::allocateProp(const char *name) {
@ -303,12 +218,10 @@ bool MediaAnalyticsItem::removeProp(const char *name) {
size_t len = strlen(name);
size_t i = findPropIndex(name, len);
if (i < mPropCount) {
Prop *prop = &mProps[i];
clearProp(prop);
mProps[i].clear();
if (i != mPropCount-1) {
// in the middle, bring last one down to fill gap
copyProp(prop, &mProps[mPropCount-1]);
clearProp(&mProps[mPropCount-1]);
mProps[i].swap(mProps[mPropCount-1]);
}
mPropCount--;
return true;
@ -316,206 +229,6 @@ bool MediaAnalyticsItem::removeProp(const char *name) {
return false;
}
// set the values
void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
Prop *prop = allocateProp(name);
if (prop != NULL) {
clearPropValue(prop);
prop->mType = kTypeInt32;
prop->u.int32Value = value;
}
}
void MediaAnalyticsItem::setInt64(MediaAnalyticsItem::Attr name, int64_t value) {
Prop *prop = allocateProp(name);
if (prop != NULL) {
clearPropValue(prop);
prop->mType = kTypeInt64;
prop->u.int64Value = value;
}
}
void MediaAnalyticsItem::setDouble(MediaAnalyticsItem::Attr name, double value) {
Prop *prop = allocateProp(name);
if (prop != NULL) {
clearPropValue(prop);
prop->mType = kTypeDouble;
prop->u.doubleValue = value;
}
}
void MediaAnalyticsItem::setCString(MediaAnalyticsItem::Attr name, const char *value) {
Prop *prop = allocateProp(name);
// any old value will be gone
if (prop != NULL) {
clearPropValue(prop);
prop->mType = kTypeCString;
prop->u.CStringValue = strdup(value);
}
}
void MediaAnalyticsItem::setRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
Prop *prop = allocateProp(name);
if (prop != NULL) {
clearPropValue(prop);
prop->mType = kTypeRate;
prop->u.rate.count = count;
prop->u.rate.duration = duration;
}
}
// find/add/set fused into a single operation
void MediaAnalyticsItem::addInt32(MediaAnalyticsItem::Attr name, int32_t value) {
Prop *prop = allocateProp(name);
if (prop == NULL) {
return;
}
switch (prop->mType) {
case kTypeInt32:
prop->u.int32Value += value;
break;
default:
clearPropValue(prop);
prop->mType = kTypeInt32;
prop->u.int32Value = value;
break;
}
}
void MediaAnalyticsItem::addInt64(MediaAnalyticsItem::Attr name, int64_t value) {
Prop *prop = allocateProp(name);
if (prop == NULL) {
return;
}
switch (prop->mType) {
case kTypeInt64:
prop->u.int64Value += value;
break;
default:
clearPropValue(prop);
prop->mType = kTypeInt64;
prop->u.int64Value = value;
break;
}
}
void MediaAnalyticsItem::addRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
Prop *prop = allocateProp(name);
if (prop == NULL) {
return;
}
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;
}
}
void MediaAnalyticsItem::addDouble(MediaAnalyticsItem::Attr name, double value) {
Prop *prop = allocateProp(name);
if (prop == NULL) {
return;
}
switch (prop->mType) {
case kTypeDouble:
prop->u.doubleValue += value;
break;
default:
clearPropValue(prop);
prop->mType = kTypeDouble;
prop->u.doubleValue = value;
break;
}
}
// find & extract values
bool MediaAnalyticsItem::getInt32(MediaAnalyticsItem::Attr name, int32_t *value) {
Prop *prop = findProp(name);
if (prop == NULL || prop->mType != kTypeInt32) {
return false;
}
if (value != NULL) {
*value = prop->u.int32Value;
}
return true;
}
bool MediaAnalyticsItem::getInt64(MediaAnalyticsItem::Attr name, int64_t *value) {
Prop *prop = findProp(name);
if (prop == NULL || prop->mType != kTypeInt64) {
return false;
}
if (value != NULL) {
*value = prop->u.int64Value;
}
return true;
}
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;
}
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 name, char **value) {
Prop *prop = findProp(name);
if (prop == NULL || prop->mType != kTypeCString) {
return false;
}
if (value != NULL) {
*value = strdup(prop->u.CStringValue);
}
return true;
}
bool MediaAnalyticsItem::getString(MediaAnalyticsItem::Attr name, std::string *value) {
Prop *prop = findProp(name);
if (prop == NULL || prop->mType != kTypeCString) {
return false;
}
if (value != NULL) {
// std::string makes a copy for us
*value = prop->u.CStringValue;
}
return true;
}
// remove indicated keys and their values
// return value is # keys removed
int32_t MediaAnalyticsItem::filter(int n, MediaAnalyticsItem::Attr attrs[]) {
@ -533,12 +246,12 @@ int32_t MediaAnalyticsItem::filter(int n, MediaAnalyticsItem::Attr attrs[]) {
} else if (j+1 == mPropCount) {
// last one, shorten
zapped++;
clearProp(&mProps[j]);
mProps[j].clear();
mPropCount--;
} else {
// in the middle, bring last one down and shorten
zapped++;
clearProp(&mProps[j]);
mProps[j].clear();
mProps[j] = mProps[mPropCount-1];
mPropCount--;
}
@ -556,13 +269,13 @@ int32_t MediaAnalyticsItem::filterNot(int n, MediaAnalyticsItem::Attr attrs[]) {
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);
if (prop->isNamed(attrs[j])) {
prop->clear();
zapped++;
if (i != (ssize_t)(mPropCount-1)) {
*prop = mProps[mPropCount-1];
}
initProp(&mProps[mPropCount-1]);
mProps[mPropCount-1].clear();
mPropCount--;
break;
}
@ -577,63 +290,6 @@ int32_t MediaAnalyticsItem::filter(MediaAnalyticsItem::Attr name) {
return filter(1, &name);
}
// handle individual items/properties stored within the class
//
void MediaAnalyticsItem::initProp(Prop *prop) {
if (prop != NULL) {
prop->mName = NULL;
prop->mNameLen = 0;
prop->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);
LOG_ALWAYS_FATAL_IF(p == NULL,
"failed malloc() duping property '%s' (len %zu)",
dst->mName, dst->mNameLen);
memcpy (p, src->mName, dst->mNameLen + 1);
dst->mName = (const char *) p;
}
if (dst->mType == kTypeCString) {
dst->u.CStringValue = strdup(src->u.CStringValue);
}
}
bool MediaAnalyticsItem::growProps(int increment)
{
if (increment <= 0) {
@ -644,7 +300,7 @@ bool MediaAnalyticsItem::growProps(int increment)
if (ni != NULL) {
for (int i = mPropSize; i < nsize; i++) {
initProp(&ni[i]);
new (&ni[i]) Prop(); // placement new
}
mProps = ni;
mPropSize = nsize;
@ -679,11 +335,8 @@ int32_t MediaAnalyticsItem::readFromParcel0(const Parcel& data) {
mUid = data.readInt32();
mPkgName = data.readCString();
mPkgVersionCode = data.readInt64();
mSessionID = data.readInt64();
// We no longer pay attention to user setting of finalized, BUT it's
// still part of the wire packet -- so read & discard.
mFinalized = data.readInt32();
mFinalized = 1;
mTimestamp = data.readInt64();
int count = data.readInt32();
@ -744,41 +397,14 @@ int32_t MediaAnalyticsItem::writeToParcel0(Parcel *data) {
data->writeInt32(mUid);
data->writeCString(mPkgName.c_str());
data->writeInt64(mPkgVersionCode);
data->writeInt64(mSessionID);
data->writeInt32(mFinalized);
data->writeInt64(mTimestamp);
// set of items
int count = mPropCount;
const size_t count = mPropCount;
data->writeInt32(count);
for (int i = 0 ; i < count; i++ ) {
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;
}
for (size_t i = 0 ; i < count; i++ ) {
mProps[i].writeToParcel(data);
}
return 0;
}
@ -821,9 +447,7 @@ std::string MediaAnalyticsItem::toString(int version) const {
// 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(":");
snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
result.append(buffer);
result.append(":0:"); // sessionID
snprintf(buffer, sizeof(buffer), "%d:", mUid);
result.append(buffer);
@ -842,7 +466,7 @@ std::string MediaAnalyticsItem::toString(int version) const {
}
result.append(buffer);
snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
snprintf(buffer, sizeof(buffer), "%d:", 0 /* finalized */); // TODO: remove this.
result.append(buffer);
snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mTimestamp);
result.append(buffer);
@ -852,39 +476,8 @@ std::string MediaAnalyticsItem::toString(int version) const {
snprintf(buffer, sizeof(buffer), "%d:", count);
result.append(buffer);
for (int i = 0 ; i < count; i++ ) {
Prop *prop = &mProps[i];
switch (prop->mType) {
case MediaAnalyticsItem::kTypeInt32:
snprintf(buffer,sizeof(buffer),
"%s=%d:", prop->mName, prop->u.int32Value);
break;
case MediaAnalyticsItem::kTypeInt64:
snprintf(buffer,sizeof(buffer),
"%s=%" PRId64 ":", prop->mName, prop->u.int64Value);
break;
case MediaAnalyticsItem::kTypeDouble:
snprintf(buffer,sizeof(buffer),
"%s=%e:", prop->mName, prop->u.doubleValue);
break;
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);
// XXX: sanitize string for ':' '='
result.append(prop->u.CStringValue);
buffer[0] = ':';
buffer[1] = '\0';
break;
default:
ALOGE("to_String bad item type: %d for %s",
prop->mType, prop->mName);
break;
}
result.append(buffer);
mProps[i].toString(buffer, sizeof(buffer));
result.append(buffer);
}
if (version == PROTO_V0) {
@ -899,23 +492,12 @@ std::string MediaAnalyticsItem::toString(int version) const {
// for the lazy, we offer methods that finds the service and
// calls the appropriate daemon
bool MediaAnalyticsItem::selfrecord() {
return selfrecord(false);
}
bool MediaAnalyticsItem::selfrecord(bool forcenew) {
if (DEBUG_API) {
std::string p = this->toString();
ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
}
ALOGD_IF(DEBUG_API, "%s: delivering %s", __func__, this->toString().c_str());
sp<IMediaAnalyticsService> svc = getInstance();
if (svc != NULL) {
MediaAnalyticsItem::SessionID_t newid = svc->submit(this, forcenew);
if (newid == SessionIDInvalid) {
std::string p = this->toString();
ALOGW("Failed to record: %s [forcenew=%d]", p.c_str(), forcenew);
status_t status = svc->submit(this);
if (status != NO_ERROR) {
ALOGW("%s: failed to record: %s", __func__, this->toString().c_str());
return false;
}
return true;
@ -924,29 +506,33 @@ bool MediaAnalyticsItem::selfrecord(bool forcenew) {
}
}
// get a connection we can reuse for most of our lifetime
// static
sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
static Mutex sInitMutex;
static int remainingBindAttempts = SVC_TRIES;
//static
bool MediaAnalyticsItem::isEnabled() {
int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
// 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.
// We do this only for a select set of UIDs
// The sepolicy protection is still in place, we just want a faster
// response from this specific, small set of uids.
// This is checked only once in the lifetime of the process.
const uid_t uid = getuid();
switch (uid) {
case AID_RADIO: // telephony subsystem, RIL
return false;
}
int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
if (enabled == -1) {
enabled = property_get_int32(MediaAnalyticsItem::EnabledPropertyPersist, -1);
}
if (enabled == -1) {
enabled = MediaAnalyticsItem::EnabledProperty_default;
}
if (enabled <= 0) {
return false;
}
return true;
return enabled > 0;
}
// monitor health of our connection to the metrics service
class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
virtual void binderDied(const wp<IBinder> &) {
@ -955,83 +541,56 @@ class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
}
};
static sp<MediaMetricsDeathNotifier> sNotifier = NULL;
static sp<MediaMetricsDeathNotifier> sNotifier;
// static
sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
static std::mutex sServiceMutex;
static int sRemainingBindAttempts = SVC_TRIES;
// static
void MediaAnalyticsItem::dropInstance() {
Mutex::Autolock _l(sInitMutex);
remainingBindAttempts = SVC_TRIES;
sAnalyticsService = NULL;
std::lock_guard _l(sServiceMutex);
sRemainingBindAttempts = SVC_TRIES;
sAnalyticsService = nullptr;
}
//static
sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
static const char *servicename = "media.metrics";
int enabled = isEnabled();
static const bool enabled = isEnabled(); // singleton initialized
if (enabled == false) {
if (DEBUG_SERVICEACCESS) {
ALOGD("disabled");
}
return NULL;
}
// 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.
// We do this only for a select set of UIDs
// The sepolicy protection is still in place, we just want a faster
// response from this specific, small set of uids.
{
uid_t uid = getuid();
switch (uid) {
case AID_RADIO: // telephony subsystem, RIL
return NULL;
break;
default:
// let sepolicy deny access if appropriate
break;
}
}
{
Mutex::Autolock _l(sInitMutex);
ALOGD_IF(DEBUG_SERVICEACCESS, "disabled");
return nullptr;
}
std::lock_guard _l(sServiceMutex);
// think of remainingBindAttempts as telling us whether service == nullptr because
// (1) we haven't tried to initialize it yet
// (2) we've tried to initialize it, but failed.
if (sAnalyticsService == nullptr && sRemainingBindAttempts > 0) {
const char *badness = "";
// think of remainingBindAttempts 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 && remainingBindAttempts > 0) {
sp<IServiceManager> sm = defaultServiceManager();
if (sm != NULL) {
sp<IBinder> binder = sm->getService(String16(servicename));
if (binder != NULL) {
sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
if (sNotifier != NULL) {
sNotifier = NULL;
}
sNotifier = new MediaMetricsDeathNotifier();
binder->linkToDeath(sNotifier);
} else {
badness = "did not find service";
}
sp<IServiceManager> sm = defaultServiceManager();
if (sm != nullptr) {
sp<IBinder> binder = sm->getService(String16(servicename));
if (binder != nullptr) {
sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
sNotifier = new MediaMetricsDeathNotifier();
binder->linkToDeath(sNotifier);
} else {
badness = "No Service Manager access";
badness = "did not find service";
}
if (sAnalyticsService == NULL) {
if (remainingBindAttempts > 0) {
remainingBindAttempts--;
}
if (DEBUG_SERVICEACCESS) {
ALOGD("Unable to bind to service %s: %s", servicename, badness);
}
} else {
badness = "No Service Manager access";
}
if (sAnalyticsService == nullptr) {
if (sRemainingBindAttempts > 0) {
sRemainingBindAttempts--;
}
ALOGD_IF(DEBUG_SERVICEACCESS, "%s: unable to bind to service %s: %s",
__func__, servicename, badness);
}
return sAnalyticsService;
}
return sAnalyticsService;
}
// merge the info from 'incoming' into this record.
@ -1042,8 +601,6 @@ bool MediaAnalyticsItem::merge(MediaAnalyticsItem *incoming) {
// 'this' should never be missing both of them...
if (mKey.empty()) {
mKey = incoming->mKey;
} else if (mSessionID == 0) {
mSessionID = incoming->mSessionID;
}
// for each attribute from 'incoming', resolve appropriately
@ -1064,12 +621,12 @@ bool MediaAnalyticsItem::merge(MediaAnalyticsItem *incoming) {
// no oprop, so we insert the new one
oprop = allocateProp(p);
if (oprop != NULL) {
copyProp(oprop, iprop);
*oprop = *iprop;
} else {
ALOGW("dropped property '%s'", iprop->mName);
}
} else {
copyProp(oprop, iprop);
*oprop = *iprop;
}
}
@ -1242,5 +799,58 @@ bool MediaAnalyticsItem::dumpAttributes(char **pbuffer, size_t *plength) {
return false;
}
void MediaAnalyticsItem::Prop::writeToParcel(Parcel *data) const
{
data->writeCString(mName);
data->writeInt32(mType);
switch (mType) {
case kTypeInt32:
data->writeInt32(u.int32Value);
break;
case kTypeInt64:
data->writeInt64(u.int64Value);
break;
case kTypeDouble:
data->writeDouble(u.doubleValue);
break;
case kTypeRate:
data->writeInt64(u.rate.count);
data->writeInt64(u.rate.duration);
break;
case kTypeCString:
data->writeCString(u.CStringValue);
break;
default:
ALOGE("%s: found bad type: %d, name %s", __func__, mType, mName);
break;
}
}
void MediaAnalyticsItem::Prop::toString(char *buffer, size_t length) const {
switch (mType) {
case kTypeInt32:
snprintf(buffer, length, "%s=%d:", mName, u.int32Value);
break;
case MediaAnalyticsItem::kTypeInt64:
snprintf(buffer, length, "%s=%lld:", mName, (long long)u.int64Value);
break;
case MediaAnalyticsItem::kTypeDouble:
snprintf(buffer, length, "%s=%e:", mName, u.doubleValue);
break;
case MediaAnalyticsItem::kTypeRate:
snprintf(buffer, length, "%s=%lld/%lld:",
mName, (long long)u.rate.count, (long long)u.rate.duration);
break;
case MediaAnalyticsItem::kTypeCString:
// TODO sanitize string for ':' '='
snprintf(buffer, length, "%s=%s:", mName, u.CStringValue);
break;
default:
ALOGE("%s: bad item type: %d for %s", __func__, mType, mName);
if (length > 0) buffer[0] = 0;
break;
}
}
} // namespace android

@ -39,25 +39,16 @@ class IMediaAnalyticsService: public IInterface
public:
DECLARE_META_INTERFACE(MediaAnalyticsService);
/**
* Returns a unique sessionID to use across multiple requests;
* 'unique' is within this device, since last reboot.
*/
virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() = 0;
/**
* Submits the indicated record to the mediaanalytics service, where
* it will be merged (if appropriate) with incomplete records that
* share the same key and sessionID.
*
* \param item the item to submit.
* \param forcenew marks any matching incomplete record as complete before
* inserting this new record.
*
* \return the sessionID associated with that item or
* MediaAnalyticsItem::SessionIDInvalid on failure.
* \return status which is negative if an error is detected (some errors
may be silent and return 0 - success).
*/
virtual MediaAnalyticsItem::SessionID_t submit(MediaAnalyticsItem *item, bool forcenew) = 0;
virtual status_t submit(MediaAnalyticsItem *item) = 0;
};
// ----------------------------------------------------------------------------
@ -69,6 +60,11 @@ public:
const Parcel& data,
Parcel* reply,
uint32_t flags = 0) override;
protected:
// Internal call where release is true if the service is to delete the item.
virtual status_t submitInternal(
MediaAnalyticsItem *item, bool release) = 0;
};
}; // namespace android

@ -38,14 +38,10 @@ class Parcel;
//
class MediaAnalyticsItem {
friend class MediaMetricsJNI; // TODO: remove this access
friend class MediaMetricsDeathNotifier; // for dropInstance
friend class MediaAnalyticsService;
friend class IMediaAnalyticsService;
friend class MediaMetricsJNI;
friend class MetricsSummarizer;
friend class MediaMetricsDeathNotifier;
public:
public:
enum Type {
kTypeNone = 0,
@ -56,20 +52,14 @@ class MediaAnalyticsItem {
kTypeRate = 5,
};
// 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 std::string Key;
static const Key kKeyNone; // ""
static const Key kKeyAny; // "*"
// 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
using Key = std::string;
static constexpr const char * const kKeyNone = "none";
static constexpr const char * const kKeyAny = "any";
// Attr: names for attributes within a record
// format "prop1" or "prop/subprop"
@ -84,14 +74,12 @@ class MediaAnalyticsItem {
PROTO_LAST = PROTO_V1,
};
private:
// use the ::create() method instead
MediaAnalyticsItem();
MediaAnalyticsItem(Key);
MediaAnalyticsItem(const MediaAnalyticsItem&);
MediaAnalyticsItem &operator=(const MediaAnalyticsItem&);
public:
// T must be convertible to mKey
template <typename T>
explicit MediaAnalyticsItem(T key)
: mKey(key) { }
MediaAnalyticsItem(const MediaAnalyticsItem&) = delete;
MediaAnalyticsItem &operator=(const MediaAnalyticsItem&) = delete;
static MediaAnalyticsItem* create(Key key);
static MediaAnalyticsItem* create();
@ -102,16 +90,6 @@ class MediaAnalyticsItem {
// access functions for the class
~MediaAnalyticsItem();
// 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();
MediaAnalyticsItem *dup();
@ -124,35 +102,91 @@ class MediaAnalyticsItem {
// # of attributes in the record
int32_t count() const;
// set values appropriately
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".
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);
bool getString(Attr, std::string *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);
template<typename S, typename T>
MediaAnalyticsItem &set(S key, T value) {
allocateProp(key)->set(value);
return *this;
}
// set values appropriately
MediaAnalyticsItem &setInt32(Attr key, int32_t value) {
return set(key, value);
}
MediaAnalyticsItem &setInt64(Attr key, int64_t value) {
return set(key, value);
}
MediaAnalyticsItem &setDouble(Attr key, double value) {
return set(key, value);
}
MediaAnalyticsItem &setRate(Attr key, int64_t count, int64_t duration) {
return set(key, std::make_pair(count, duration));
}
MediaAnalyticsItem &setCString(Attr key, const char *value) {
return set(key, value);
}
// fused get/add/set; if attr wasn't there, it's a simple set.
// type-mismatch counts as "wasn't there".
template<typename S, typename T>
MediaAnalyticsItem &add(S key, T value) {
allocateProp(key)->add(value);
return *this;
}
MediaAnalyticsItem &addInt32(Attr key, int32_t value) {
return add(key, value);
}
MediaAnalyticsItem &addInt64(Attr key, int64_t value) {
return add(key, value);
}
MediaAnalyticsItem &addDouble(Attr key, double value) {
return add(key, value);
}
MediaAnalyticsItem &addRate(Attr key, int64_t count, int64_t duration) {
return add(key, std::make_pair(count, duration));
}
// find & extract values
// return indicates whether attr exists (and thus value filled in)
// NULL parameter value suppresses storage of value.
template<typename S, typename T>
bool get(S key, T *value) const {
Prop *prop = findProp(key);
return prop != nullptr && prop->get(value);
}
bool getInt32(Attr key, int32_t *value) const {
return get(key, value);
}
bool getInt64(Attr key, int64_t *value) const {
return get(key, value);
}
bool getDouble(Attr key, double *value) const {
return get(key, value);
}
bool getRate(Attr key, int64_t *count, int64_t *duration, double *rate) const {
std::pair<int64_t, int64_t> value;
if (!get(key, &value)) return false;
if (count != nullptr) *count = value.first;
if (duration != nullptr) *duration = value.second;
if (rate != nullptr) {
if (value.second != 0) {
*rate = (double)value.first / value.second; // TODO: isn't INF OK?
} else {
*rate = 0.;
}
}
return true;
}
// Caller owns the returned string
bool getCString(Attr key, char **value) const {
return get(key, value);
}
bool getString(Attr key, std::string *value) const {
return get(key, value);
}
// Deliver the item to MediaMetrics
bool selfrecord();
// remove indicated attributes and their values
@ -212,64 +246,261 @@ class MediaAnalyticsItem {
// caller continues to own 'incoming'
bool merge(MediaAnalyticsItem *incoming);
// enabled 1, disabled 0
static const char * const EnabledProperty;
static const char * const EnabledPropertyPersist;
static const int EnabledProperty_default;
// enabled 1, disabled 0
static constexpr const char * const EnabledProperty = "media.metrics.enabled";
static constexpr const char * const EnabledPropertyPersist = "persist.media.metrics.enabled";
static const int EnabledProperty_default = 1;
private:
// to help validate that A doesn't mess with B's records
pid_t mPid;
uid_t mUid;
std::string mPkgName;
int64_t mPkgVersionCode;
// let's reuse a binder connection
static sp<IMediaAnalyticsService> sAnalyticsService;
static sp<IMediaAnalyticsService> getInstance();
static void dropInstance();
// 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;
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);
};
// let's reuse a binder connection
static sp<IMediaAnalyticsService> sAnalyticsService;
static sp<IMediaAnalyticsService> getInstance();
static void dropInstance();
class Prop {
friend class MediaMetricsJNI; // TODO: remove this access
public:
Prop() = default;
Prop(const Prop& other) {
*this = other;
}
Prop& operator=(const Prop& other) {
if (other.mName != nullptr) {
mName = strdup(other.mName);
} else {
mName = nullptr;
}
mNameLen = other.mNameLen;
mType = other.mType;
switch (mType) {
case kTypeInt32:
u.int32Value = other.u.int32Value;
break;
case kTypeInt64:
u.int64Value = other.u.int64Value;
break;
case kTypeDouble:
u.doubleValue = other.u.doubleValue;
break;
case kTypeCString:
u.CStringValue = strdup(other.u.CStringValue);
break;
case kTypeRate:
u.rate = {other.u.rate.count, other.u.rate.duration};
break;
case kTypeNone:
break;
default:
// abort?
break;
}
return *this;
}
void clear() {
free(mName);
mName = nullptr;
mNameLen = 0;
clearValue();
}
void clearValue() {
if (mType == kTypeCString) {
free(u.CStringValue);
u.CStringValue = nullptr;
}
mType = kTypeNone;
}
Type getType() const {
return mType;
}
const char *getName() const {
return mName;
}
void swap(Prop& other) {
std::swap(mName, other.mName);
std::swap(mNameLen, other.mNameLen);
std::swap(mType, other.mType);
std::swap(u, other.u);
}
void setName(const char *name, size_t len) {
free(mName);
if (name != nullptr) {
mName = (char *)malloc(len + 1);
mNameLen = len;
strncpy(mName, name, len);
mName[len] = 0;
} else {
mName = nullptr;
mNameLen = 0;
}
}
bool isNamed(const char *name, size_t len) const {
return len == mNameLen && memcmp(name, mName, len) == 0;
}
// TODO: remove duplicate but different definition
bool isNamed(const char *name) const {
return strcmp(name, mName) == 0;
}
template <typename T> bool get(T *value) const = delete;
template <>
bool get(int32_t *value) const {
if (mType != kTypeInt32) return false;
if (value != nullptr) *value = u.int32Value;
return true;
}
template <>
bool get(int64_t *value) const {
if (mType != kTypeInt64) return false;
if (value != nullptr) *value = u.int64Value;
return true;
}
template <>
bool get(double *value) const {
if (mType != kTypeDouble) return false;
if (value != nullptr) *value = u.doubleValue;
return true;
}
template <>
bool get(char** value) const {
if (mType != kTypeCString) return false;
if (value != nullptr) *value = strdup(u.CStringValue);
return true;
}
template <>
bool get(std::string* value) const {
if (mType != kTypeCString) return false;
if (value != nullptr) *value = u.CStringValue;
return true;
}
template <>
bool get(std::pair<int64_t, int64_t> *value) const {
if (mType != kTypeRate) return false;
if (value != nullptr) {
value->first = u.rate.count;
value->second = u.rate.duration;
}
return true;
}
template <typename T> void set(const T& value) = delete;
template <>
void set(const int32_t& value) {
mType = kTypeInt32;
u.int32Value = value;
}
template <>
void set(const int64_t& value) {
mType = kTypeInt64;
u.int64Value = value;
}
template <>
void set(const double& value) {
mType = kTypeDouble;
u.doubleValue = value;
}
template <>
void set(const char* const& value) {
if (mType == kTypeCString) {
free(u.CStringValue);
} else {
mType = kTypeCString;
}
if (value == nullptr) {
u.CStringValue = nullptr;
} else {
u.CStringValue = strdup(value);
}
}
template <>
void set(const std::pair<int64_t, int64_t> &value) {
mType = kTypeRate;
u.rate = {value.first, value.second};
}
template <typename T> void add(const T& value) = delete;
template <>
void add(const int32_t& value) {
if (mType == kTypeInt32) {
u.int32Value += value;
} else {
mType = kTypeInt32;
u.int32Value = value;
}
}
template <>
void add(const int64_t& value) {
if (mType == kTypeInt64) {
u.int64Value += value;
} else {
mType = kTypeInt64;
u.int64Value = value;
}
}
template <>
void add(const double& value) {
if (mType == kTypeDouble) {
u.doubleValue += value;
} else {
mType = kTypeDouble;
u.doubleValue = value;
}
}
template <>
void add(const std::pair<int64_t, int64_t>& value) {
if (mType == kTypeRate) {
u.rate.count += value.first;
u.rate.duration += value.second;
} else {
mType = kTypeRate;
u.rate = {value.first, value.second};
}
}
void writeToParcel(Parcel *data) const;
void toString(char *buffer, size_t length) const;
// TODO: make private
// private:
char *mName = nullptr;
size_t mNameLen = 0; // the strlen(), doesn't include the null
Type mType = kTypeNone;
union {
int32_t int32Value;
int64_t int64Value;
double doubleValue;
char *CStringValue;
struct { int64_t count, duration; } rate;
} u;
};
size_t findPropIndex(const char *name, size_t len) const;
Prop *findProp(const char *name) const;
void initProp(Prop *item);
void clearProp(Prop *item);
void clearPropValue(Prop *item);
void copyProp(Prop *dst, const Prop *src);
enum {
kGrowProps = 10
};
bool growProps(int increment = kGrowProps);
size_t findPropIndex(const char *name, size_t len);
Prop *findProp(const char *name);
Prop *allocateProp(const char *name);
bool removeProp(const char *name);
size_t mPropCount;
size_t mPropSize;
Prop *mProps;
size_t mPropCount = 0;
size_t mPropSize = 0;
Prop *mProps = nullptr;
pid_t mPid = -1;
uid_t mUid = -1;
std::string mPkgName;
int64_t mPkgVersionCode = 0;
Key mKey{kKeyNone};
nsecs_t mTimestamp = 0;
};
} // namespace android

@ -66,19 +66,8 @@ MediaAnalyticsService::~MediaAnalyticsService()
mItems.clear();
}
MediaAnalyticsItem::SessionID_t MediaAnalyticsService::generateUniqueSessionID() {
return ++mLastSessionID;
}
// TODO: consider removing forcenew.
MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(
MediaAnalyticsItem *item, bool forcenew __unused)
status_t MediaAnalyticsService::submitInternal(MediaAnalyticsItem *item, bool release)
{
// fill in a sessionID if we do not yet have one
if (item->getSessionID() <= MediaAnalyticsItem::SessionIDNone) {
item->setSessionID(generateUniqueSessionID());
}
// we control these, generally not trusting user input
nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
// round nsecs to seconds
@ -136,8 +125,8 @@ MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(
// validate the record; we discard if we don't like it
if (isContentValid(item, isTrusted) == false) {
delete item;
return MediaAnalyticsItem::SessionIDInvalid;
if (release) delete item;
return PERMISSION_DENIED;
}
// XXX: if we have a sessionid in the new record, look to make
@ -145,18 +134,17 @@ MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(
if (item->count() == 0) {
ALOGV("%s: dropping empty record...", __func__);
delete item;
return MediaAnalyticsItem::SessionIDInvalid;
if (release) delete item;
return BAD_VALUE;
}
// send to statsd
extern bool dump2Statsd(MediaAnalyticsItem *item); // extern hook
(void)dump2Statsd(item); // failure should be logged in function.
// keep our copy
const MediaAnalyticsItem::SessionID_t id = item->getSessionID();
if (!release) item = item->dup();
saveItem(item);
return id;
return NO_ERROR;
}
status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)

@ -35,31 +35,28 @@ public:
~MediaAnalyticsService() override;
/**
* Submits the indicated record to the mediaanalytics service, where
* it will be merged (if appropriate) with incomplete records that
* share the same key and sessionid.
* Submits the indicated record to the mediaanalytics service.
*
* \param item the item to submit.
* \param forcenew marks any matching incomplete record as complete before
* inserting this new record.
*
* \return the sessionID associated with that item or
* MediaAnalyticsItem::SessionIDInvalid on failure.
*
* BEWARE: When called directly on the service (not from the binder interface),
* the caller surrenders ownership of item, MediaAnalyticsService will delete
* even on error. The binder interface does not take ownership.
* TODO: fix this inconsistency with the binder RPC interface.
* \return status failure, which is negative on binder transaction failure.
* As the transaction is one-way, remote failures will not be reported.
*/
MediaAnalyticsItem::SessionID_t submit(MediaAnalyticsItem *item, bool forcenew) override;
status_t submit(MediaAnalyticsItem *item) override {
return submitInternal(item, false /* release */);
}
status_t dump(int fd, const Vector<String16>& args) override;
static constexpr const char * const kServiceName = "media.metrics";
protected:
// Internal call where release is true if ownership of item is transferred
// to the service (that is, the service will eventually delete the item).
status_t submitInternal(MediaAnalyticsItem *item, bool release) override;
private:
void processExpirations();
MediaAnalyticsItem::SessionID_t generateUniqueSessionID();
// input validation after arrival from client
static bool isContentValid(const MediaAnalyticsItem *item, bool isTrusted);
bool isRateLimited(MediaAnalyticsItem *) const;
@ -86,8 +83,6 @@ private:
const size_t mMaxRecordsExpiredAtOnce;
const int mDumpProtoDefault;
std::atomic<MediaAnalyticsItem::SessionID_t> mLastSessionID{};
class UidInfo {
public:
void setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion);

@ -30,26 +30,148 @@ TEST(mediametrics_tests, instantiate) {
sp mediaMetrics = new MediaAnalyticsService();
status_t status;
// NOTE: submission of items to MediaMetrics releases ownership, even on error.
// random keys ignored when empty
status = mediaMetrics->submit(MediaAnalyticsItem::create("random_key"), false);
ASSERT_EQ(MediaAnalyticsItem::SessionIDInvalid, status);
std::unique_ptr<MediaAnalyticsItem> random_key(MediaAnalyticsItem::create("random_key"));
status = mediaMetrics->submit(random_key.get());
ASSERT_EQ(PERMISSION_DENIED, status);
// random keys ignored with data
auto random_key = MediaAnalyticsItem::create("random_key");
random_key->setInt32("foo", 10);
status = mediaMetrics->submit(random_key, false);
ASSERT_EQ(MediaAnalyticsItem::SessionIDInvalid, status);
status = mediaMetrics->submit(random_key.get());
ASSERT_EQ(PERMISSION_DENIED, status);
// known keys ignored if empty
status = mediaMetrics->submit(MediaAnalyticsItem::create("audiotrack"), false);
ASSERT_EQ(MediaAnalyticsItem::SessionIDInvalid, status);
std::unique_ptr<MediaAnalyticsItem> audiotrack_key(MediaAnalyticsItem::create("audiotrack"));
status = mediaMetrics->submit(audiotrack_key.get());
ASSERT_EQ(BAD_VALUE, status);
// known keys not ignored if not empty
audiotrack_key->addInt32("foo", 10);
status = mediaMetrics->submit(audiotrack_key.get());
ASSERT_EQ(NO_ERROR, status);
auto audiotrack = MediaAnalyticsItem::create("audiotrack");
audiotrack->addInt32("foo", 10);
status = mediaMetrics->submit(audiotrack, false);
ASSERT_GT(status, MediaAnalyticsItem::SessionIDNone);
/*
// fluent style that goes directly to mediametrics
ASSERT_EQ(true, MediaAnalyticsItem("audiorecord")
.setInt32("value", 2)
.addInt32("bar", 1)
.addInt32("value", 3)
.selfrecord());
*/
mediaMetrics->dump(fileno(stdout), {} /* args */);
}
TEST(mediametrics_tests, item_manipulation) {
MediaAnalyticsItem item("audiorecord");
item.setInt32("value", 2).addInt32("bar", 3).addInt32("value", 4);
int32_t i32;
ASSERT_TRUE(item.getInt32("value", &i32));
ASSERT_EQ(6, i32);
ASSERT_TRUE(item.getInt32("bar", &i32));
ASSERT_EQ(3, i32);
item.setInt64("big", INT64_MAX).setInt64("smaller", INT64_MAX - 1).addInt64("smaller", -2);
int64_t i64;
ASSERT_TRUE(item.getInt64("big", &i64));
ASSERT_EQ(INT64_MAX, i64);
ASSERT_TRUE(item.getInt64("smaller", &i64));
ASSERT_EQ(INT64_MAX - 3, i64);
item.setDouble("precise", 10.5).setDouble("small", 0.125).addDouble("precise", 0.25);
double d;
ASSERT_TRUE(item.getDouble("precise", &d));
ASSERT_EQ(10.75, d);
ASSERT_TRUE(item.getDouble("small", &d));
ASSERT_EQ(0.125, d);
char *s;
item.setCString("name", "Frank").setCString("mother", "June").setCString("mother", "July");
ASSERT_TRUE(item.getCString("name", &s));
ASSERT_EQ(0, strcmp(s, "Frank"));
free(s);
ASSERT_TRUE(item.getCString("mother", &s));
ASSERT_EQ(0, strcmp(s, "July")); // "July" overwrites "June"
free(s);
item.setRate("burgersPerHour", 5, 2);
int64_t b, h;
ASSERT_TRUE(item.getRate("burgersPerHour", &b, &h, &d));
ASSERT_EQ(5, b);
ASSERT_EQ(2, h);
ASSERT_EQ(2.5, d);
item.addRate("burgersPerHour", 4, 2);
ASSERT_TRUE(item.getRate("burgersPerHour", &b, &h, &d));
ASSERT_EQ(9, b);
ASSERT_EQ(4, h);
ASSERT_EQ(2.25, d);
printf("item: %s\n", item.toString().c_str());
fflush(stdout);
sp mediaMetrics = new MediaAnalyticsService();
status_t status = mediaMetrics->submit(&item);
ASSERT_EQ(NO_ERROR, status);
mediaMetrics->dump(fileno(stdout), {} /* args */);
}
TEST(mediametrics_tests, superbig_item) {
MediaAnalyticsItem item("TheBigOne");
constexpr size_t count = 10000;
for (size_t i = 0; i < count; ++i) {
item.setInt32(std::to_string(i).c_str(), i);
}
for (size_t i = 0; i < count; ++i) {
int32_t i32;
ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
ASSERT_EQ((int32_t)i, i32);
}
}
TEST(mediametrics_tests, superbig_item_removal) {
MediaAnalyticsItem item("TheOddBigOne");
constexpr size_t count = 10000;
for (size_t i = 0; i < count; ++i) {
item.setInt32(std::to_string(i).c_str(), i);
}
for (size_t i = 0; i < count; i += 2) {
item.filter(std::to_string(i).c_str()); // filter out all the evens.
}
for (size_t i = 0; i < count; ++i) {
int32_t i32;
if (i & 1) { // check to see that only the odds are left.
ASSERT_TRUE(item.getInt32(std::to_string(i).c_str(), &i32));
ASSERT_EQ((int32_t)i, i32);
} else {
ASSERT_FALSE(item.getInt32(std::to_string(i).c_str(), &i32));
}
}
}
TEST(mediametrics_tests, item_transmutation) {
MediaAnalyticsItem item("Alchemist's Stone");
item.setInt64("convert", 123);
int64_t i64;
ASSERT_TRUE(item.getInt64("convert", &i64));
ASSERT_EQ(123, i64);
item.addInt32("convert", 2); // changes type of 'convert' from i64 to i32 (and re-init).
ASSERT_FALSE(item.getInt64("convert", &i64)); // should be false, no value in i64.
int32_t i32;
ASSERT_TRUE(item.getInt32("convert", &i32)); // check it is i32 and 2 (123 is discarded).
ASSERT_EQ(2, i32);
}

Loading…
Cancel
Save