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
MediaAnalyticsItem()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 
MediaAnalyticsItem(MediaAnalyticsItem::Key key)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 
~MediaAnalyticsItem()84 MediaAnalyticsItem::~MediaAnalyticsItem() {
85     if (DEBUG_ALLOCATIONS) {
86         ALOGD("Destroy  MediaAnalyticsItem @ %p", this);
87     }
88     clear();
89 }
90 
clear()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
dup()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
setFinalized(bool value)139 MediaAnalyticsItem &MediaAnalyticsItem::setFinalized(bool value) {
140     mFinalized = value;
141     return *this;
142 }
143 
getFinalized() const144 bool MediaAnalyticsItem::getFinalized() const {
145     return mFinalized;
146 }
147 
setSessionID(MediaAnalyticsItem::SessionID_t id)148 MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
149     mSessionID = id;
150     return *this;
151 }
152 
getSessionID() const153 MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const {
154     return mSessionID;
155 }
156 
generateSessionID()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 
clearSessionID()172 MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() {
173     mSessionID = MediaAnalyticsItem::SessionIDNone;
174     return *this;
175 }
176 
setTimestamp(nsecs_t ts)177 MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) {
178     mTimestamp = ts;
179     return *this;
180 }
181 
getTimestamp() const182 nsecs_t MediaAnalyticsItem::getTimestamp() const {
183     return mTimestamp;
184 }
185 
setPid(pid_t pid)186 MediaAnalyticsItem &MediaAnalyticsItem::setPid(pid_t pid) {
187     mPid = pid;
188     return *this;
189 }
190 
getPid() const191 pid_t MediaAnalyticsItem::getPid() const {
192     return mPid;
193 }
194 
setUid(uid_t uid)195 MediaAnalyticsItem &MediaAnalyticsItem::setUid(uid_t uid) {
196     mUid = uid;
197     return *this;
198 }
199 
getUid() const200 uid_t MediaAnalyticsItem::getUid() const {
201     return mUid;
202 }
203 
204 // this key is for the overall record -- "codec", "player", "drm", etc
setKey(MediaAnalyticsItem::Key key)205 MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
206     mKey = key;
207     return *this;
208 }
209 
getKey()210 MediaAnalyticsItem::Key MediaAnalyticsItem::getKey() {
211     return mKey;
212 }
213 
214 // number of attributes we have in this record
count() const215 int32_t MediaAnalyticsItem::count() const {
216     return mPropCount;
217 }
218 
219 // find the proper entry in the list
findPropIndex(const char * name,size_t len)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 
findProp(const char * name)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 
setName(const char * name,size_t len)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
allocateProp(const char * name)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
setInt32(MediaAnalyticsItem::Attr name,int32_t value)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 
setInt64(MediaAnalyticsItem::Attr name,int64_t value)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 
setDouble(MediaAnalyticsItem::Attr name,double value)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 
setCString(MediaAnalyticsItem::Attr name,const char * value)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 
setRate(MediaAnalyticsItem::Attr name,int64_t count,int64_t duration)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
addInt32(MediaAnalyticsItem::Attr name,int32_t value)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 
addInt64(MediaAnalyticsItem::Attr name,int64_t value)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 
addRate(MediaAnalyticsItem::Attr name,int64_t count,int64_t duration)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 
addDouble(MediaAnalyticsItem::Attr name,double value)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
getInt32(MediaAnalyticsItem::Attr name,int32_t * value)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 
getInt64(MediaAnalyticsItem::Attr name,int64_t * value)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 
getRate(MediaAnalyticsItem::Attr name,int64_t * count,int64_t * duration,double * rate)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 
getDouble(MediaAnalyticsItem::Attr name,double * value)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
getCString(MediaAnalyticsItem::Attr name,char ** value)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
filter(int n,MediaAnalyticsItem::Attr attrs[])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
filterNot(int n,MediaAnalyticsItem::Attr attrs[])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)
filter(MediaAnalyticsItem::Attr name)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 
initProp(Prop * prop)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 
clearProp(Prop * prop)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 
clearPropValue(Prop * prop)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 
copyProp(Prop * dst,const Prop * src)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 
growProps(int increment)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 
readFromParcel(const Parcel & data)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 
writeToParcel(Parcel * data)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 
toString()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
selfrecord()723 bool MediaAnalyticsItem::selfrecord() {
724     return selfrecord(false);
725 }
726 
selfrecord(bool forcenew)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
isEnabled()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
getInstance()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
merge(MediaAnalyticsItem * incoming)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