Home | History | Annotate | Download | only in libmediametrics
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #undef LOG_TAG
     18 #define LOG_TAG "MediaAnalyticsItem"
     19 
     20 #include <inttypes.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <sys/types.h>
     24 
     25 #include <binder/Parcel.h>
     26 #include <utils/Errors.h>
     27 #include <utils/Log.h>
     28 #include <utils/Mutex.h>
     29 #include <utils/SortedVector.h>
     30 #include <utils/threads.h>
     31 
     32 #include <media/stagefright/foundation/AString.h>
     33 
     34 #include <binder/IServiceManager.h>
     35 #include <media/IMediaAnalyticsService.h>
     36 #include <media/MediaAnalyticsItem.h>
     37 #include <private/android_filesystem_config.h>
     38 
     39 namespace android {
     40 
     41 #define DEBUG_SERVICEACCESS     0
     42 #define DEBUG_API               0
     43 #define DEBUG_ALLOCATIONS       0
     44 
     45 // after this many failed attempts, we stop trying [from this process] and just say that
     46 // the service is off.
     47 #define SVC_TRIES               2
     48 
     49 // the few universal keys we have
     50 const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyAny  = "any";
     51 const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyNone  = "none";
     52 
     53 const char * const MediaAnalyticsItem::EnabledProperty  = "media.metrics.enabled";
     54 const char * const MediaAnalyticsItem::EnabledPropertyPersist  = "persist.media.metrics.enabled";
     55 const int MediaAnalyticsItem::EnabledProperty_default  = 1;
     56 
     57 
     58 // access functions for the class
     59 MediaAnalyticsItem::MediaAnalyticsItem()
     60     : mPid(-1),
     61       mUid(-1),
     62       mSessionID(MediaAnalyticsItem::SessionIDNone),
     63       mTimestamp(0),
     64       mFinalized(0),
     65       mPropCount(0), mPropSize(0), mProps(NULL)
     66 {
     67     mKey = MediaAnalyticsItem::kKeyNone;
     68 }
     69 
     70 MediaAnalyticsItem::MediaAnalyticsItem(MediaAnalyticsItem::Key key)
     71     : mPid(-1),
     72       mUid(-1),
     73       mSessionID(MediaAnalyticsItem::SessionIDNone),
     74       mTimestamp(0),
     75       mFinalized(0),
     76       mPropCount(0), mPropSize(0), mProps(NULL)
     77 {
     78     if (DEBUG_ALLOCATIONS) {
     79         ALOGD("Allocate MediaAnalyticsItem @ %p", this);
     80     }
     81     mKey = key;
     82 }
     83 
     84 MediaAnalyticsItem::~MediaAnalyticsItem() {
     85     if (DEBUG_ALLOCATIONS) {
     86         ALOGD("Destroy  MediaAnalyticsItem @ %p", this);
     87     }
     88     clear();
     89 }
     90 
     91 void MediaAnalyticsItem::clear() {
     92 
     93     // clean allocated storage from key
     94     mKey.clear();
     95 
     96     // clean various major parameters
     97     mSessionID = MediaAnalyticsItem::SessionIDNone;
     98 
     99     // clean attributes
    100     // contents of the attributes
    101     for (size_t i = 0 ; i < mPropSize; i++ ) {
    102         clearProp(&mProps[i]);
    103     }
    104     // the attribute records themselves
    105     if (mProps != NULL) {
    106         free(mProps);
    107         mProps = NULL;
    108     }
    109     mPropSize = 0;
    110     mPropCount = 0;
    111 
    112     return;
    113 }
    114 
    115 // make a deep copy of myself
    116 MediaAnalyticsItem *MediaAnalyticsItem::dup() {
    117     MediaAnalyticsItem *dst = new MediaAnalyticsItem(this->mKey);
    118 
    119     if (dst != NULL) {
    120         // key as part of constructor
    121         dst->mPid = this->mPid;
    122         dst->mUid = this->mUid;
    123         dst->mSessionID = this->mSessionID;
    124         dst->mTimestamp = this->mTimestamp;
    125         dst->mFinalized = this->mFinalized;
    126 
    127         // properties aka attributes
    128         dst->growProps(this->mPropCount);
    129         for(size_t i=0;i<mPropCount;i++) {
    130             copyProp(&dst->mProps[i], &this->mProps[i]);
    131         }
    132         dst->mPropCount = this->mPropCount;
    133     }
    134 
    135     return dst;
    136 }
    137 
    138 // so clients can send intermediate values to be overlaid later
    139 MediaAnalyticsItem &MediaAnalyticsItem::setFinalized(bool value) {
    140     mFinalized = value;
    141     return *this;
    142 }
    143 
    144 bool MediaAnalyticsItem::getFinalized() const {
    145     return mFinalized;
    146 }
    147 
    148 MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
    149     mSessionID = id;
    150     return *this;
    151 }
    152 
    153 MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const {
    154     return mSessionID;
    155 }
    156 
    157 MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::generateSessionID() {
    158 
    159     if (mSessionID == SessionIDNone) {
    160         // get one from the server
    161         MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
    162         sp<IMediaAnalyticsService> svc = getInstance();
    163         if (svc != NULL) {
    164             newid = svc->generateUniqueSessionID();
    165         }
    166         mSessionID = newid;
    167     }
    168 
    169     return mSessionID;
    170 }
    171 
    172 MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() {
    173     mSessionID = MediaAnalyticsItem::SessionIDNone;
    174     return *this;
    175 }
    176 
    177 MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) {
    178     mTimestamp = ts;
    179     return *this;
    180 }
    181 
    182 nsecs_t MediaAnalyticsItem::getTimestamp() const {
    183     return mTimestamp;
    184 }
    185 
    186 MediaAnalyticsItem &MediaAnalyticsItem::setPid(pid_t pid) {
    187     mPid = pid;
    188     return *this;
    189 }
    190 
    191 pid_t MediaAnalyticsItem::getPid() const {
    192     return mPid;
    193 }
    194 
    195 MediaAnalyticsItem &MediaAnalyticsItem::setUid(uid_t uid) {
    196     mUid = uid;
    197     return *this;
    198 }
    199 
    200 uid_t MediaAnalyticsItem::getUid() const {
    201     return mUid;
    202 }
    203 
    204 // this key is for the overall record -- "codec", "player", "drm", etc
    205 MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
    206     mKey = key;
    207     return *this;
    208 }
    209 
    210 MediaAnalyticsItem::Key MediaAnalyticsItem::getKey() {
    211     return mKey;
    212 }
    213 
    214 // number of attributes we have in this record
    215 int32_t MediaAnalyticsItem::count() const {
    216     return mPropCount;
    217 }
    218 
    219 // find the proper entry in the list
    220 size_t MediaAnalyticsItem::findPropIndex(const char *name, size_t len)
    221 {
    222     size_t i = 0;
    223     for (; i < mPropCount; i++) {
    224         Prop *prop = &mProps[i];
    225         if (prop->mNameLen != len) {
    226             continue;
    227         }
    228         if (memcmp(name, prop->mName, len) == 0) {
    229             break;
    230         }
    231     }
    232     return i;
    233 }
    234 
    235 MediaAnalyticsItem::Prop *MediaAnalyticsItem::findProp(const char *name) {
    236     size_t len = strlen(name);
    237     size_t i = findPropIndex(name, len);
    238     if (i < mPropCount) {
    239         return &mProps[i];
    240     }
    241     return NULL;
    242 }
    243 
    244 void MediaAnalyticsItem::Prop::setName(const char *name, size_t len) {
    245     mNameLen = len;
    246     mName = (const char *) malloc(len+1);
    247     memcpy ((void *)mName, name, len+1);
    248 }
    249 
    250 // used only as part of a storing operation
    251 MediaAnalyticsItem::Prop *MediaAnalyticsItem::allocateProp(const char *name) {
    252     size_t len = strlen(name);
    253     size_t i = findPropIndex(name, len);
    254     Prop *prop;
    255 
    256     if (i < mPropCount) {
    257         prop = &mProps[i];
    258     } else {
    259         if (i == mPropSize) {
    260             growProps();
    261             // XXX: verify success
    262         }
    263         i = mPropCount++;
    264         prop = &mProps[i];
    265         prop->setName(name, len);
    266     }
    267 
    268     return prop;
    269 }
    270 
    271 // set the values
    272 void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
    273     Prop *prop = allocateProp(name);
    274     prop->mType = kTypeInt32;
    275     prop->u.int32Value = value;
    276 }
    277 
    278 void MediaAnalyticsItem::setInt64(MediaAnalyticsItem::Attr name, int64_t value) {
    279     Prop *prop = allocateProp(name);
    280     prop->mType = kTypeInt64;
    281     prop->u.int64Value = value;
    282 }
    283 
    284 void MediaAnalyticsItem::setDouble(MediaAnalyticsItem::Attr name, double value) {
    285     Prop *prop = allocateProp(name);
    286     prop->mType = kTypeDouble;
    287     prop->u.doubleValue = value;
    288 }
    289 
    290 void MediaAnalyticsItem::setCString(MediaAnalyticsItem::Attr name, const char *value) {
    291 
    292     Prop *prop = allocateProp(name);
    293     // any old value will be gone
    294     prop->mType = kTypeCString;
    295     prop->u.CStringValue = strdup(value);
    296 }
    297 
    298 void MediaAnalyticsItem::setRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
    299     Prop *prop = allocateProp(name);
    300     prop->mType = kTypeRate;
    301     prop->u.rate.count = count;
    302     prop->u.rate.duration = duration;
    303 }
    304 
    305 
    306 // find/add/set fused into a single operation
    307 void MediaAnalyticsItem::addInt32(MediaAnalyticsItem::Attr name, int32_t value) {
    308     Prop *prop = allocateProp(name);
    309     switch (prop->mType) {
    310         case kTypeInt32:
    311             prop->u.int32Value += value;
    312             break;
    313         default:
    314             clearPropValue(prop);
    315             prop->mType = kTypeInt32;
    316             prop->u.int32Value = value;
    317             break;
    318     }
    319 }
    320 
    321 void MediaAnalyticsItem::addInt64(MediaAnalyticsItem::Attr name, int64_t value) {
    322     Prop *prop = allocateProp(name);
    323     switch (prop->mType) {
    324         case kTypeInt64:
    325             prop->u.int64Value += value;
    326             break;
    327         default:
    328             clearPropValue(prop);
    329             prop->mType = kTypeInt64;
    330             prop->u.int64Value = value;
    331             break;
    332     }
    333 }
    334 
    335 void MediaAnalyticsItem::addRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
    336     Prop *prop = allocateProp(name);
    337     switch (prop->mType) {
    338         case kTypeRate:
    339             prop->u.rate.count += count;
    340             prop->u.rate.duration += duration;
    341             break;
    342         default:
    343             clearPropValue(prop);
    344             prop->mType = kTypeRate;
    345             prop->u.rate.count = count;
    346             prop->u.rate.duration = duration;
    347             break;
    348     }
    349 }
    350 
    351 void MediaAnalyticsItem::addDouble(MediaAnalyticsItem::Attr name, double value) {
    352     Prop *prop = allocateProp(name);
    353     switch (prop->mType) {
    354         case kTypeDouble:
    355             prop->u.doubleValue += value;
    356             break;
    357         default:
    358             clearPropValue(prop);
    359             prop->mType = kTypeDouble;
    360             prop->u.doubleValue = value;
    361             break;
    362     }
    363 }
    364 
    365 // find & extract values
    366 bool MediaAnalyticsItem::getInt32(MediaAnalyticsItem::Attr name, int32_t *value) {
    367     Prop *prop = findProp(name);
    368     if (prop == NULL || prop->mType != kTypeInt32) {
    369         return false;
    370     }
    371     if (value != NULL) {
    372         *value = prop->u.int32Value;
    373     }
    374     return true;
    375 }
    376 
    377 bool MediaAnalyticsItem::getInt64(MediaAnalyticsItem::Attr name, int64_t *value) {
    378     Prop *prop = findProp(name);
    379     if (prop == NULL || prop->mType != kTypeInt64) {
    380         return false;
    381     }
    382     if (value != NULL) {
    383         *value = prop->u.int64Value;
    384     }
    385     return true;
    386 }
    387 
    388 bool MediaAnalyticsItem::getRate(MediaAnalyticsItem::Attr name, int64_t *count, int64_t *duration, double *rate) {
    389     Prop *prop = findProp(name);
    390     if (prop == NULL || prop->mType != kTypeRate) {
    391         return false;
    392     }
    393     if (count != NULL) {
    394         *count = prop->u.rate.count;
    395     }
    396     if (duration != NULL) {
    397         *duration = prop->u.rate.duration;
    398     }
    399     if (rate != NULL) {
    400         double r = 0.0;
    401         if (prop->u.rate.duration != 0) {
    402             r = prop->u.rate.count / (double) prop->u.rate.duration;
    403         }
    404         *rate = r;
    405     }
    406     return true;
    407 }
    408 
    409 bool MediaAnalyticsItem::getDouble(MediaAnalyticsItem::Attr name, double *value) {
    410     Prop *prop = findProp(name);
    411     if (prop == NULL || prop->mType != kTypeDouble) {
    412         return false;
    413     }
    414     if (value != NULL) {
    415         *value = prop->u.doubleValue;
    416     }
    417     return true;
    418 }
    419 
    420 // caller responsible for the returned string
    421 bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr name, char **value) {
    422     Prop *prop = findProp(name);
    423     if (prop == NULL || prop->mType != kTypeDouble) {
    424         return false;
    425     }
    426     if (value != NULL) {
    427         *value = strdup(prop->u.CStringValue);
    428     }
    429     return true;
    430 }
    431 
    432 // remove indicated keys and their values
    433 // return value is # keys removed
    434 int32_t MediaAnalyticsItem::filter(int n, MediaAnalyticsItem::Attr attrs[]) {
    435     int zapped = 0;
    436     if (attrs == NULL || n <= 0) {
    437         return -1;
    438     }
    439     for (ssize_t i = 0 ; i < n ;  i++) {
    440         const char *name = attrs[i];
    441         size_t len = strlen(name);
    442         size_t j = findPropIndex(name, len);
    443         if (j >= mPropCount) {
    444             // not there
    445             continue;
    446         } else if (j+1 == mPropCount) {
    447             // last one, shorten
    448             zapped++;
    449             clearProp(&mProps[j]);
    450             mPropCount--;
    451         } else {
    452             // in the middle, bring last one down and shorten
    453             zapped++;
    454             clearProp(&mProps[j]);
    455             mProps[j] = mProps[mPropCount-1];
    456             mPropCount--;
    457         }
    458     }
    459     return zapped;
    460 }
    461 
    462 // remove any keys NOT in the provided list
    463 // return value is # keys removed
    464 int32_t MediaAnalyticsItem::filterNot(int n, MediaAnalyticsItem::Attr attrs[]) {
    465     int zapped = 0;
    466     if (attrs == NULL || n <= 0) {
    467         return -1;
    468     }
    469     for (ssize_t i = mPropCount-1 ; i >=0 ;  i--) {
    470         Prop *prop = &mProps[i];
    471         for (ssize_t j = 0; j < n ; j++) {
    472             if (strcmp(prop->mName, attrs[j]) == 0) {
    473                 clearProp(prop);
    474                 zapped++;
    475                 if (i != (ssize_t)(mPropCount-1)) {
    476                     *prop = mProps[mPropCount-1];
    477                 }
    478                 initProp(&mProps[mPropCount-1]);
    479                 mPropCount--;
    480                 break;
    481             }
    482         }
    483     }
    484     return zapped;
    485 }
    486 
    487 // remove a single key
    488 // return value is 0 (not found) or 1 (found and removed)
    489 int32_t MediaAnalyticsItem::filter(MediaAnalyticsItem::Attr name) {
    490     return filter(1, &name);
    491 }
    492 
    493 // handle individual items/properties stored within the class
    494 //
    495 
    496 void MediaAnalyticsItem::initProp(Prop *prop) {
    497     if (prop != NULL) {
    498         prop->mName = NULL;
    499         prop->mNameLen = 0;
    500 
    501         prop->mType = kTypeNone;
    502     }
    503 }
    504 
    505 void MediaAnalyticsItem::clearProp(Prop *prop)
    506 {
    507     if (prop != NULL) {
    508         if (prop->mName != NULL) {
    509             free((void *)prop->mName);
    510             prop->mName = NULL;
    511             prop->mNameLen = 0;
    512         }
    513 
    514         clearPropValue(prop);
    515     }
    516 }
    517 
    518 void MediaAnalyticsItem::clearPropValue(Prop *prop)
    519 {
    520     if (prop != NULL) {
    521         if (prop->mType == kTypeCString && prop->u.CStringValue != NULL) {
    522             free(prop->u.CStringValue);
    523             prop->u.CStringValue = NULL;
    524         }
    525         prop->mType = kTypeNone;
    526     }
    527 }
    528 
    529 void MediaAnalyticsItem::copyProp(Prop *dst, const Prop *src)
    530 {
    531     // get rid of any pointers in the dst
    532     clearProp(dst);
    533 
    534     *dst = *src;
    535 
    536     // fix any pointers that we blindly copied, so we have our own copies
    537     if (dst->mName) {
    538         void *p =  malloc(dst->mNameLen + 1);
    539         memcpy (p, src->mName, dst->mNameLen + 1);
    540         dst->mName = (const char *) p;
    541     }
    542     if (dst->mType == kTypeCString) {
    543         dst->u.CStringValue = strdup(src->u.CStringValue);
    544     }
    545 }
    546 
    547 void MediaAnalyticsItem::growProps(int increment)
    548 {
    549     if (increment <= 0) {
    550         increment = kGrowProps;
    551     }
    552     int nsize = mPropSize + increment;
    553     Prop *ni = (Prop *)realloc(mProps, sizeof(Prop) * nsize);
    554 
    555     if (ni != NULL) {
    556         for (int i = mPropSize; i < nsize; i++) {
    557             initProp(&ni[i]);
    558         }
    559         mProps = ni;
    560         mPropSize = nsize;
    561     }
    562 }
    563 
    564 // Parcel / serialize things for binder calls
    565 //
    566 
    567 int32_t MediaAnalyticsItem::readFromParcel(const Parcel& data) {
    568     // into 'this' object
    569     // .. we make a copy of the string to put away.
    570     mKey = data.readCString();
    571     mSessionID = data.readInt64();
    572     mFinalized = data.readInt32();
    573     mTimestamp = data.readInt64();
    574 
    575     int count = data.readInt32();
    576     for (int i = 0; i < count ; i++) {
    577             MediaAnalyticsItem::Attr attr = data.readCString();
    578             int32_t ztype = data.readInt32();
    579                 switch (ztype) {
    580                     case MediaAnalyticsItem::kTypeInt32:
    581                             setInt32(attr, data.readInt32());
    582                             break;
    583                     case MediaAnalyticsItem::kTypeInt64:
    584                             setInt64(attr, data.readInt64());
    585                             break;
    586                     case MediaAnalyticsItem::kTypeDouble:
    587                             setDouble(attr, data.readDouble());
    588                             break;
    589                     case MediaAnalyticsItem::kTypeCString:
    590                             setCString(attr, data.readCString());
    591                             break;
    592                     case MediaAnalyticsItem::kTypeRate:
    593                             {
    594                                 int64_t count = data.readInt64();
    595                                 int64_t duration = data.readInt64();
    596                                 setRate(attr, count, duration);
    597                             }
    598                             break;
    599                     default:
    600                             ALOGE("reading bad item type: %d, idx %d",
    601                                   ztype, i);
    602                             return -1;
    603                 }
    604     }
    605 
    606     return 0;
    607 }
    608 
    609 int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) {
    610     if (data == NULL) return -1;
    611 
    612 
    613     data->writeCString(mKey.c_str());
    614     data->writeInt64(mSessionID);
    615     data->writeInt32(mFinalized);
    616     data->writeInt64(mTimestamp);
    617 
    618     // set of items
    619     int count = mPropCount;
    620     data->writeInt32(count);
    621     for (int i = 0 ; i < count; i++ ) {
    622             Prop *prop = &mProps[i];
    623             data->writeCString(prop->mName);
    624             data->writeInt32(prop->mType);
    625             switch (prop->mType) {
    626                 case MediaAnalyticsItem::kTypeInt32:
    627                         data->writeInt32(prop->u.int32Value);
    628                         break;
    629                 case MediaAnalyticsItem::kTypeInt64:
    630                         data->writeInt64(prop->u.int64Value);
    631                         break;
    632                 case MediaAnalyticsItem::kTypeDouble:
    633                         data->writeDouble(prop->u.doubleValue);
    634                         break;
    635                 case MediaAnalyticsItem::kTypeRate:
    636                         data->writeInt64(prop->u.rate.count);
    637                         data->writeInt64(prop->u.rate.duration);
    638                         break;
    639                 case MediaAnalyticsItem::kTypeCString:
    640                         data->writeCString(prop->u.CStringValue);
    641                         break;
    642                 default:
    643                         ALOGE("found bad Prop type: %d, idx %d, name %s",
    644                               prop->mType, i, prop->mName);
    645                         break;
    646             }
    647     }
    648 
    649     return 0;
    650 }
    651 
    652 
    653 AString MediaAnalyticsItem::toString() {
    654 
    655     AString result = "(";
    656     char buffer[512];
    657 
    658     // same order as we spill into the parcel, although not required
    659     // key+session are our primary matching criteria
    660     //RBE ALOGD("mKey.c_str");
    661     result.append(mKey.c_str());
    662     //RBE ALOGD("post-mKey.c_str");
    663     result.append(":");
    664     snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
    665     result.append(buffer);
    666 
    667     // we need these internally, but don't want to upload them
    668     snprintf(buffer, sizeof(buffer), "%d:%d", mUid, mPid);
    669     result.append(buffer);
    670 
    671     snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
    672     result.append(buffer);
    673     snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mTimestamp);
    674     result.append(buffer);
    675 
    676     // set of items
    677     int count = mPropCount;
    678     snprintf(buffer, sizeof(buffer), "%d:", count);
    679     result.append(buffer);
    680     for (int i = 0 ; i < count; i++ ) {
    681             Prop *prop = &mProps[i];
    682             switch (prop->mType) {
    683                 case MediaAnalyticsItem::kTypeInt32:
    684                         snprintf(buffer,sizeof(buffer),
    685                         "%s=%d:", prop->mName, prop->u.int32Value);
    686                         break;
    687                 case MediaAnalyticsItem::kTypeInt64:
    688                         snprintf(buffer,sizeof(buffer),
    689                         "%s=%" PRId64 ":", prop->mName, prop->u.int64Value);
    690                         break;
    691                 case MediaAnalyticsItem::kTypeDouble:
    692                         snprintf(buffer,sizeof(buffer),
    693                         "%s=%e:", prop->mName, prop->u.doubleValue);
    694                         break;
    695                 case MediaAnalyticsItem::kTypeRate:
    696                         snprintf(buffer,sizeof(buffer),
    697                         "%s=%" PRId64 "/%" PRId64 ":", prop->mName,
    698                         prop->u.rate.count, prop->u.rate.duration);
    699                         break;
    700                 case MediaAnalyticsItem::kTypeCString:
    701                         snprintf(buffer,sizeof(buffer), "%s=", prop->mName);
    702                         result.append(buffer);
    703                         // XXX: sanitize string for ':' '='
    704                         result.append(prop->u.CStringValue);
    705                         buffer[0] = ':';
    706                         buffer[1] = '\0';
    707                         break;
    708                 default:
    709                         ALOGE("to_String bad item type: %d for %s",
    710                               prop->mType, prop->mName);
    711                         break;
    712             }
    713             result.append(buffer);
    714     }
    715 
    716     result.append(")");
    717 
    718     return result;
    719 }
    720 
    721 // for the lazy, we offer methods that finds the service and
    722 // calls the appropriate daemon
    723 bool MediaAnalyticsItem::selfrecord() {
    724     return selfrecord(false);
    725 }
    726 
    727 bool MediaAnalyticsItem::selfrecord(bool forcenew) {
    728 
    729     if (DEBUG_API) {
    730         AString p = this->toString();
    731         ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
    732     }
    733 
    734     sp<IMediaAnalyticsService> svc = getInstance();
    735 
    736     if (svc != NULL) {
    737         svc->submit(this, forcenew);
    738         return true;
    739     } else {
    740         AString p = this->toString();
    741         ALOGD("Unable to record: %s [forcenew=%d]", p.c_str(), forcenew);
    742         return false;
    743     }
    744 }
    745 
    746 // get a connection we can reuse for most of our lifetime
    747 // static
    748 sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
    749 static Mutex sInitMutex;
    750 
    751 //static
    752 bool MediaAnalyticsItem::isEnabled() {
    753     int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
    754 
    755     if (enabled == -1) {
    756         enabled = property_get_int32(MediaAnalyticsItem::EnabledPropertyPersist, -1);
    757     }
    758     if (enabled == -1) {
    759         enabled = MediaAnalyticsItem::EnabledProperty_default;
    760     }
    761     if (enabled <= 0) {
    762         return false;
    763     }
    764     return true;
    765 }
    766 
    767 //static
    768 sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
    769     static const char *servicename = "media.metrics";
    770     static int tries_remaining = SVC_TRIES;
    771     int enabled = isEnabled();
    772 
    773     if (enabled == false) {
    774         if (DEBUG_SERVICEACCESS) {
    775                 ALOGD("disabled");
    776         }
    777         return NULL;
    778     }
    779 
    780     // completely skip logging from certain UIDs. We do this here
    781     // to avoid the multi-second timeouts while we learn that
    782     // sepolicy will not let us find the service.
    783     // We do this only for a select set of UIDs
    784     // The sepolicy protection is still in place, we just want a faster
    785     // response from this specific, small set of uids.
    786     {
    787         uid_t uid = getuid();
    788         switch (uid) {
    789             case AID_RADIO:     // telephony subsystem, RIL
    790                 return NULL;
    791                 break;
    792             default:
    793                 // let sepolicy deny access if appropriate
    794                 break;
    795         }
    796     }
    797 
    798     {
    799         Mutex::Autolock _l(sInitMutex);
    800         const char *badness = "";
    801 
    802         // think of tries_remaining as telling us whether service==NULL because
    803         // (1) we haven't tried to initialize it yet
    804         // (2) we've tried to initialize it, but failed.
    805         if (sAnalyticsService == NULL && tries_remaining > 0) {
    806             sp<IServiceManager> sm = defaultServiceManager();
    807             if (sm != NULL) {
    808                 sp<IBinder> binder = sm->getService(String16(servicename));
    809                 if (binder != NULL) {
    810                     sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
    811                 } else {
    812                     badness = "did not find service";
    813                 }
    814             } else {
    815                 badness = "No Service Manager access";
    816             }
    817 
    818             if (sAnalyticsService == NULL) {
    819                 if (tries_remaining > 0) {
    820                     tries_remaining--;
    821                 }
    822                 if (DEBUG_SERVICEACCESS) {
    823                     ALOGD("Unable to bind to service %s: %s", servicename, badness);
    824                 }
    825             }
    826         }
    827 
    828         return sAnalyticsService;
    829     }
    830 }
    831 
    832 
    833 // merge the info from 'incoming' into this record.
    834 // we finish with a union of this+incoming and special handling for collisions
    835 bool MediaAnalyticsItem::merge(MediaAnalyticsItem *incoming) {
    836 
    837     // if I don't have key or session id, take them from incoming
    838     // 'this' should never be missing both of them...
    839     if (mKey.empty()) {
    840         mKey = incoming->mKey;
    841     } else if (mSessionID == 0) {
    842         mSessionID = incoming->mSessionID;
    843     }
    844 
    845     // we always take the more recent 'finalized' value
    846     setFinalized(incoming->getFinalized());
    847 
    848     // for each attribute from 'incoming', resolve appropriately
    849     int nattr = incoming->mPropCount;
    850     for (int i = 0 ; i < nattr; i++ ) {
    851         Prop *iprop = &incoming->mProps[i];
    852         Prop *oprop = findProp(iprop->mName);
    853         const char *p = iprop->mName;
    854         size_t len = strlen(p);
    855         char semantic = p[len-1];
    856 
    857         if (oprop == NULL) {
    858             // no oprop, so we insert the new one
    859             oprop = allocateProp(p);
    860             copyProp(oprop, iprop);
    861         } else {
    862             // merge iprop into oprop
    863             switch (semantic) {
    864                 case '<':       // first  aka keep old)
    865                     /* nop */
    866                     break;
    867 
    868                 default:        // default is 'last'
    869                 case '>':       // last (aka keep new)
    870                     copyProp(oprop, iprop);
    871                     break;
    872 
    873                 case '+':       /* sum */
    874                     // XXX validate numeric types, sum in place
    875                     break;
    876 
    877             }
    878         }
    879     }
    880 
    881     // not sure when we'd return false...
    882     return true;
    883 }
    884 
    885 } // namespace android
    886 
    887