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 <binder/IServiceManager.h>
33 #include <media/IMediaAnalyticsService.h>
34 #include <media/MediaAnalyticsItem.h>
35 #include <private/android_filesystem_config.h>
36 
37 namespace android {
38 
39 #define DEBUG_SERVICEACCESS     0
40 #define DEBUG_API               0
41 #define DEBUG_ALLOCATIONS       0
42 
43 // after this many failed attempts, we stop trying [from this process] and just say that
44 // the service is off.
45 #define SVC_TRIES               2
46 
47 // the few universal keys we have
48 const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyAny  = "any";
49 const MediaAnalyticsItem::Key MediaAnalyticsItem::kKeyNone  = "none";
50 
51 const char * const MediaAnalyticsItem::EnabledProperty  = "media.metrics.enabled";
52 const char * const MediaAnalyticsItem::EnabledPropertyPersist  = "persist.media.metrics.enabled";
53 const int MediaAnalyticsItem::EnabledProperty_default  = 1;
54 
55 // So caller doesn't need to know size of allocated space
create()56 MediaAnalyticsItem *MediaAnalyticsItem::create()
57 {
58     return MediaAnalyticsItem::create(kKeyNone);
59 }
60 
create(MediaAnalyticsItem::Key key)61 MediaAnalyticsItem *MediaAnalyticsItem::create(MediaAnalyticsItem::Key key)
62 {
63     MediaAnalyticsItem *item = new MediaAnalyticsItem(key);
64     return item;
65 }
66 
convert(mediametrics_handle_t handle)67 MediaAnalyticsItem* MediaAnalyticsItem::convert(mediametrics_handle_t handle) {
68     MediaAnalyticsItem *item = (android::MediaAnalyticsItem *) handle;
69     return item;
70 }
71 
convert(MediaAnalyticsItem * item)72 mediametrics_handle_t MediaAnalyticsItem::convert(MediaAnalyticsItem *item ) {
73     mediametrics_handle_t handle = (mediametrics_handle_t) item;
74     return handle;
75 }
76 
77 // access functions for the class
MediaAnalyticsItem()78 MediaAnalyticsItem::MediaAnalyticsItem()
79     : mPid(-1),
80       mUid(-1),
81       mPkgVersionCode(0),
82       mSessionID(MediaAnalyticsItem::SessionIDNone),
83       mTimestamp(0),
84       mFinalized(1),
85       mPropCount(0), mPropSize(0), mProps(NULL)
86 {
87     mKey = MediaAnalyticsItem::kKeyNone;
88 }
89 
MediaAnalyticsItem(MediaAnalyticsItem::Key key)90 MediaAnalyticsItem::MediaAnalyticsItem(MediaAnalyticsItem::Key key)
91     : mPid(-1),
92       mUid(-1),
93       mPkgVersionCode(0),
94       mSessionID(MediaAnalyticsItem::SessionIDNone),
95       mTimestamp(0),
96       mFinalized(1),
97       mPropCount(0), mPropSize(0), mProps(NULL)
98 {
99     if (DEBUG_ALLOCATIONS) {
100         ALOGD("Allocate MediaAnalyticsItem @ %p", this);
101     }
102     mKey = key;
103 }
104 
~MediaAnalyticsItem()105 MediaAnalyticsItem::~MediaAnalyticsItem() {
106     if (DEBUG_ALLOCATIONS) {
107         ALOGD("Destroy  MediaAnalyticsItem @ %p", this);
108     }
109     clear();
110 }
111 
clear()112 void MediaAnalyticsItem::clear() {
113 
114     // clean allocated storage from key
115     mKey.clear();
116 
117     // clean various major parameters
118     mSessionID = MediaAnalyticsItem::SessionIDNone;
119 
120     // clean attributes
121     // contents of the attributes
122     for (size_t i = 0 ; i < mPropCount; i++ ) {
123         clearProp(&mProps[i]);
124     }
125     // the attribute records themselves
126     if (mProps != NULL) {
127         free(mProps);
128         mProps = NULL;
129     }
130     mPropSize = 0;
131     mPropCount = 0;
132 
133     return;
134 }
135 
136 // make a deep copy of myself
dup()137 MediaAnalyticsItem *MediaAnalyticsItem::dup() {
138     MediaAnalyticsItem *dst = new MediaAnalyticsItem(this->mKey);
139 
140     if (dst != NULL) {
141         // key as part of constructor
142         dst->mPid = this->mPid;
143         dst->mUid = this->mUid;
144         dst->mPkgName = this->mPkgName;
145         dst->mPkgVersionCode = this->mPkgVersionCode;
146         dst->mSessionID = this->mSessionID;
147         dst->mTimestamp = this->mTimestamp;
148         dst->mFinalized = this->mFinalized;
149 
150         // properties aka attributes
151         dst->growProps(this->mPropCount);
152         for(size_t i=0;i<mPropCount;i++) {
153             copyProp(&dst->mProps[i], &this->mProps[i]);
154         }
155         dst->mPropCount = this->mPropCount;
156     }
157 
158     return dst;
159 }
160 
setSessionID(MediaAnalyticsItem::SessionID_t id)161 MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
162     mSessionID = id;
163     return *this;
164 }
165 
getSessionID() const166 MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const {
167     return mSessionID;
168 }
169 
generateSessionID()170 MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::generateSessionID() {
171 
172     if (mSessionID == SessionIDNone) {
173         // get one from the server
174         MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
175         sp<IMediaAnalyticsService> svc = getInstance();
176         if (svc != NULL) {
177             newid = svc->generateUniqueSessionID();
178         }
179         mSessionID = newid;
180     }
181 
182     return mSessionID;
183 }
184 
clearSessionID()185 MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() {
186     mSessionID = MediaAnalyticsItem::SessionIDNone;
187     return *this;
188 }
189 
setTimestamp(nsecs_t ts)190 MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) {
191     mTimestamp = ts;
192     return *this;
193 }
194 
getTimestamp() const195 nsecs_t MediaAnalyticsItem::getTimestamp() const {
196     return mTimestamp;
197 }
198 
setPid(pid_t pid)199 MediaAnalyticsItem &MediaAnalyticsItem::setPid(pid_t pid) {
200     mPid = pid;
201     return *this;
202 }
203 
getPid() const204 pid_t MediaAnalyticsItem::getPid() const {
205     return mPid;
206 }
207 
setUid(uid_t uid)208 MediaAnalyticsItem &MediaAnalyticsItem::setUid(uid_t uid) {
209     mUid = uid;
210     return *this;
211 }
212 
getUid() const213 uid_t MediaAnalyticsItem::getUid() const {
214     return mUid;
215 }
216 
setPkgName(const std::string & pkgName)217 MediaAnalyticsItem &MediaAnalyticsItem::setPkgName(const std::string &pkgName) {
218     mPkgName = pkgName;
219     return *this;
220 }
221 
setPkgVersionCode(int64_t pkgVersionCode)222 MediaAnalyticsItem &MediaAnalyticsItem::setPkgVersionCode(int64_t pkgVersionCode) {
223     mPkgVersionCode = pkgVersionCode;
224     return *this;
225 }
226 
getPkgVersionCode() const227 int64_t MediaAnalyticsItem::getPkgVersionCode() const {
228     return mPkgVersionCode;
229 }
230 
231 // this key is for the overall record -- "codec", "player", "drm", etc
setKey(MediaAnalyticsItem::Key key)232 MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
233     mKey = key;
234     return *this;
235 }
236 
getKey()237 MediaAnalyticsItem::Key MediaAnalyticsItem::getKey() {
238     return mKey;
239 }
240 
241 // number of attributes we have in this record
count() const242 int32_t MediaAnalyticsItem::count() const {
243     return mPropCount;
244 }
245 
246 // find the proper entry in the list
findPropIndex(const char * name,size_t len)247 size_t MediaAnalyticsItem::findPropIndex(const char *name, size_t len)
248 {
249     size_t i = 0;
250     for (; i < mPropCount; i++) {
251         Prop *prop = &mProps[i];
252         if (prop->mNameLen != len) {
253             continue;
254         }
255         if (memcmp(name, prop->mName, len) == 0) {
256             break;
257         }
258     }
259     return i;
260 }
261 
findProp(const char * name)262 MediaAnalyticsItem::Prop *MediaAnalyticsItem::findProp(const char *name) {
263     size_t len = strlen(name);
264     size_t i = findPropIndex(name, len);
265     if (i < mPropCount) {
266         return &mProps[i];
267     }
268     return NULL;
269 }
270 
setName(const char * name,size_t len)271 void MediaAnalyticsItem::Prop::setName(const char *name, size_t len) {
272     free((void *)mName);
273     mName = (const char *) malloc(len+1);
274     LOG_ALWAYS_FATAL_IF(mName == NULL,
275                         "failed malloc() for property '%s' (len %zu)",
276                         name, len);
277     memcpy ((void *)mName, name, len+1);
278     mNameLen = len;
279 }
280 
281 // consider this "find-or-allocate".
282 // caller validates type and uses clearPropValue() accordingly
allocateProp(const char * name)283 MediaAnalyticsItem::Prop *MediaAnalyticsItem::allocateProp(const char *name) {
284     size_t len = strlen(name);
285     size_t i = findPropIndex(name, len);
286     Prop *prop;
287 
288     if (i < mPropCount) {
289         prop = &mProps[i];
290     } else {
291         if (i == mPropSize) {
292             if (growProps() == false) {
293                 ALOGE("failed allocation for new props");
294                 return NULL;
295             }
296         }
297         i = mPropCount++;
298         prop = &mProps[i];
299         prop->setName(name, len);
300     }
301 
302     return prop;
303 }
304 
305 // used within the summarizers; return whether property existed
removeProp(const char * name)306 bool MediaAnalyticsItem::removeProp(const char *name) {
307     size_t len = strlen(name);
308     size_t i = findPropIndex(name, len);
309     if (i < mPropCount) {
310         Prop *prop = &mProps[i];
311         clearProp(prop);
312         if (i != mPropCount-1) {
313             // in the middle, bring last one down to fill gap
314             copyProp(prop, &mProps[mPropCount-1]);
315             clearProp(&mProps[mPropCount-1]);
316         }
317         mPropCount--;
318         return true;
319     }
320     return false;
321 }
322 
323 // set the values
setInt32(MediaAnalyticsItem::Attr name,int32_t value)324 void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
325     Prop *prop = allocateProp(name);
326     if (prop != NULL) {
327         clearPropValue(prop);
328         prop->mType = kTypeInt32;
329         prop->u.int32Value = value;
330     }
331 }
332 
setInt64(MediaAnalyticsItem::Attr name,int64_t value)333 void MediaAnalyticsItem::setInt64(MediaAnalyticsItem::Attr name, int64_t value) {
334     Prop *prop = allocateProp(name);
335     if (prop != NULL) {
336         clearPropValue(prop);
337         prop->mType = kTypeInt64;
338         prop->u.int64Value = value;
339     }
340 }
341 
setDouble(MediaAnalyticsItem::Attr name,double value)342 void MediaAnalyticsItem::setDouble(MediaAnalyticsItem::Attr name, double value) {
343     Prop *prop = allocateProp(name);
344     if (prop != NULL) {
345         clearPropValue(prop);
346         prop->mType = kTypeDouble;
347         prop->u.doubleValue = value;
348     }
349 }
350 
setCString(MediaAnalyticsItem::Attr name,const char * value)351 void MediaAnalyticsItem::setCString(MediaAnalyticsItem::Attr name, const char *value) {
352 
353     Prop *prop = allocateProp(name);
354     // any old value will be gone
355     if (prop != NULL) {
356         clearPropValue(prop);
357         prop->mType = kTypeCString;
358         prop->u.CStringValue = strdup(value);
359     }
360 }
361 
setRate(MediaAnalyticsItem::Attr name,int64_t count,int64_t duration)362 void MediaAnalyticsItem::setRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
363     Prop *prop = allocateProp(name);
364     if (prop != NULL) {
365         clearPropValue(prop);
366         prop->mType = kTypeRate;
367         prop->u.rate.count = count;
368         prop->u.rate.duration = duration;
369     }
370 }
371 
372 
373 // find/add/set fused into a single operation
addInt32(MediaAnalyticsItem::Attr name,int32_t value)374 void MediaAnalyticsItem::addInt32(MediaAnalyticsItem::Attr name, int32_t value) {
375     Prop *prop = allocateProp(name);
376     if (prop == NULL) {
377         return;
378     }
379     switch (prop->mType) {
380         case kTypeInt32:
381             prop->u.int32Value += value;
382             break;
383         default:
384             clearPropValue(prop);
385             prop->mType = kTypeInt32;
386             prop->u.int32Value = value;
387             break;
388     }
389 }
390 
addInt64(MediaAnalyticsItem::Attr name,int64_t value)391 void MediaAnalyticsItem::addInt64(MediaAnalyticsItem::Attr name, int64_t value) {
392     Prop *prop = allocateProp(name);
393     if (prop == NULL) {
394         return;
395     }
396     switch (prop->mType) {
397         case kTypeInt64:
398             prop->u.int64Value += value;
399             break;
400         default:
401             clearPropValue(prop);
402             prop->mType = kTypeInt64;
403             prop->u.int64Value = value;
404             break;
405     }
406 }
407 
addRate(MediaAnalyticsItem::Attr name,int64_t count,int64_t duration)408 void MediaAnalyticsItem::addRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
409     Prop *prop = allocateProp(name);
410     if (prop == NULL) {
411         return;
412     }
413     switch (prop->mType) {
414         case kTypeRate:
415             prop->u.rate.count += count;
416             prop->u.rate.duration += duration;
417             break;
418         default:
419             clearPropValue(prop);
420             prop->mType = kTypeRate;
421             prop->u.rate.count = count;
422             prop->u.rate.duration = duration;
423             break;
424     }
425 }
426 
addDouble(MediaAnalyticsItem::Attr name,double value)427 void MediaAnalyticsItem::addDouble(MediaAnalyticsItem::Attr name, double value) {
428     Prop *prop = allocateProp(name);
429     if (prop == NULL) {
430         return;
431     }
432     switch (prop->mType) {
433         case kTypeDouble:
434             prop->u.doubleValue += value;
435             break;
436         default:
437             clearPropValue(prop);
438             prop->mType = kTypeDouble;
439             prop->u.doubleValue = value;
440             break;
441     }
442 }
443 
444 // find & extract values
getInt32(MediaAnalyticsItem::Attr name,int32_t * value)445 bool MediaAnalyticsItem::getInt32(MediaAnalyticsItem::Attr name, int32_t *value) {
446     Prop *prop = findProp(name);
447     if (prop == NULL || prop->mType != kTypeInt32) {
448         return false;
449     }
450     if (value != NULL) {
451         *value = prop->u.int32Value;
452     }
453     return true;
454 }
455 
getInt64(MediaAnalyticsItem::Attr name,int64_t * value)456 bool MediaAnalyticsItem::getInt64(MediaAnalyticsItem::Attr name, int64_t *value) {
457     Prop *prop = findProp(name);
458     if (prop == NULL || prop->mType != kTypeInt64) {
459         return false;
460     }
461     if (value != NULL) {
462         *value = prop->u.int64Value;
463     }
464     return true;
465 }
466 
getRate(MediaAnalyticsItem::Attr name,int64_t * count,int64_t * duration,double * rate)467 bool MediaAnalyticsItem::getRate(MediaAnalyticsItem::Attr name, int64_t *count, int64_t *duration, double *rate) {
468     Prop *prop = findProp(name);
469     if (prop == NULL || prop->mType != kTypeRate) {
470         return false;
471     }
472     if (count != NULL) {
473         *count = prop->u.rate.count;
474     }
475     if (duration != NULL) {
476         *duration = prop->u.rate.duration;
477     }
478     if (rate != NULL) {
479         double r = 0.0;
480         if (prop->u.rate.duration != 0) {
481             r = prop->u.rate.count / (double) prop->u.rate.duration;
482         }
483         *rate = r;
484     }
485     return true;
486 }
487 
getDouble(MediaAnalyticsItem::Attr name,double * value)488 bool MediaAnalyticsItem::getDouble(MediaAnalyticsItem::Attr name, double *value) {
489     Prop *prop = findProp(name);
490     if (prop == NULL || prop->mType != kTypeDouble) {
491         return false;
492     }
493     if (value != NULL) {
494         *value = prop->u.doubleValue;
495     }
496     return true;
497 }
498 
499 // caller responsible for the returned string
getCString(MediaAnalyticsItem::Attr name,char ** value)500 bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr name, char **value) {
501     Prop *prop = findProp(name);
502     if (prop == NULL || prop->mType != kTypeCString) {
503         return false;
504     }
505     if (value != NULL) {
506         *value = strdup(prop->u.CStringValue);
507     }
508     return true;
509 }
510 
getString(MediaAnalyticsItem::Attr name,std::string * value)511 bool MediaAnalyticsItem::getString(MediaAnalyticsItem::Attr name, std::string *value) {
512     Prop *prop = findProp(name);
513     if (prop == NULL || prop->mType != kTypeCString) {
514         return false;
515     }
516     if (value != NULL) {
517         // std::string makes a copy for us
518         *value = prop->u.CStringValue;
519     }
520     return true;
521 }
522 
523 // remove indicated keys and their values
524 // return value is # keys removed
filter(int n,MediaAnalyticsItem::Attr attrs[])525 int32_t MediaAnalyticsItem::filter(int n, MediaAnalyticsItem::Attr attrs[]) {
526     int zapped = 0;
527     if (attrs == NULL || n <= 0) {
528         return -1;
529     }
530     for (ssize_t i = 0 ; i < n ;  i++) {
531         const char *name = attrs[i];
532         size_t len = strlen(name);
533         size_t j = findPropIndex(name, len);
534         if (j >= mPropCount) {
535             // not there
536             continue;
537         } else if (j+1 == mPropCount) {
538             // last one, shorten
539             zapped++;
540             clearProp(&mProps[j]);
541             mPropCount--;
542         } else {
543             // in the middle, bring last one down and shorten
544             zapped++;
545             clearProp(&mProps[j]);
546             mProps[j] = mProps[mPropCount-1];
547             mPropCount--;
548         }
549     }
550     return zapped;
551 }
552 
553 // remove any keys NOT in the provided list
554 // return value is # keys removed
filterNot(int n,MediaAnalyticsItem::Attr attrs[])555 int32_t MediaAnalyticsItem::filterNot(int n, MediaAnalyticsItem::Attr attrs[]) {
556     int zapped = 0;
557     if (attrs == NULL || n <= 0) {
558         return -1;
559     }
560     for (ssize_t i = mPropCount-1 ; i >=0 ;  i--) {
561         Prop *prop = &mProps[i];
562         for (ssize_t j = 0; j < n ; j++) {
563             if (strcmp(prop->mName, attrs[j]) == 0) {
564                 clearProp(prop);
565                 zapped++;
566                 if (i != (ssize_t)(mPropCount-1)) {
567                     *prop = mProps[mPropCount-1];
568                 }
569                 initProp(&mProps[mPropCount-1]);
570                 mPropCount--;
571                 break;
572             }
573         }
574     }
575     return zapped;
576 }
577 
578 // remove a single key
579 // return value is 0 (not found) or 1 (found and removed)
filter(MediaAnalyticsItem::Attr name)580 int32_t MediaAnalyticsItem::filter(MediaAnalyticsItem::Attr name) {
581     return filter(1, &name);
582 }
583 
584 // handle individual items/properties stored within the class
585 //
586 
initProp(Prop * prop)587 void MediaAnalyticsItem::initProp(Prop *prop) {
588     if (prop != NULL) {
589         prop->mName = NULL;
590         prop->mNameLen = 0;
591 
592         prop->mType = kTypeNone;
593     }
594 }
595 
clearProp(Prop * prop)596 void MediaAnalyticsItem::clearProp(Prop *prop)
597 {
598     if (prop != NULL) {
599         if (prop->mName != NULL) {
600             free((void *)prop->mName);
601             prop->mName = NULL;
602             prop->mNameLen = 0;
603         }
604 
605         clearPropValue(prop);
606     }
607 }
608 
clearPropValue(Prop * prop)609 void MediaAnalyticsItem::clearPropValue(Prop *prop)
610 {
611     if (prop != NULL) {
612         if (prop->mType == kTypeCString && prop->u.CStringValue != NULL) {
613             free(prop->u.CStringValue);
614             prop->u.CStringValue = NULL;
615         }
616         prop->mType = kTypeNone;
617     }
618 }
619 
copyProp(Prop * dst,const Prop * src)620 void MediaAnalyticsItem::copyProp(Prop *dst, const Prop *src)
621 {
622     // get rid of any pointers in the dst
623     clearProp(dst);
624 
625     *dst = *src;
626 
627     // fix any pointers that we blindly copied, so we have our own copies
628     if (dst->mName) {
629         void *p =  malloc(dst->mNameLen + 1);
630         LOG_ALWAYS_FATAL_IF(p == NULL,
631                             "failed malloc() duping property '%s' (len %zu)",
632                             dst->mName, dst->mNameLen);
633         memcpy (p, src->mName, dst->mNameLen + 1);
634         dst->mName = (const char *) p;
635     }
636     if (dst->mType == kTypeCString) {
637         dst->u.CStringValue = strdup(src->u.CStringValue);
638     }
639 }
640 
growProps(int increment)641 bool MediaAnalyticsItem::growProps(int increment)
642 {
643     if (increment <= 0) {
644         increment = kGrowProps;
645     }
646     int nsize = mPropSize + increment;
647     Prop *ni = (Prop *)realloc(mProps, sizeof(Prop) * nsize);
648 
649     if (ni != NULL) {
650         for (int i = mPropSize; i < nsize; i++) {
651             initProp(&ni[i]);
652         }
653         mProps = ni;
654         mPropSize = nsize;
655         return true;
656     } else {
657         ALOGW("MediaAnalyticsItem::growProps fails");
658         return false;
659     }
660 }
661 
662 // Parcel / serialize things for binder calls
663 //
664 
readFromParcel(const Parcel & data)665 int32_t MediaAnalyticsItem::readFromParcel(const Parcel& data) {
666     int32_t version = data.readInt32();
667 
668     switch(version) {
669         case 0:
670           return readFromParcel0(data);
671           break;
672         default:
673           ALOGE("Unsupported MediaAnalyticsItem Parcel version: %d", version);
674           return -1;
675     }
676 }
677 
readFromParcel0(const Parcel & data)678 int32_t MediaAnalyticsItem::readFromParcel0(const Parcel& data) {
679     // into 'this' object
680     // .. we make a copy of the string to put away.
681     mKey = data.readCString();
682     mPid = data.readInt32();
683     mUid = data.readInt32();
684     mPkgName = data.readCString();
685     mPkgVersionCode = data.readInt64();
686     mSessionID = data.readInt64();
687     // We no longer pay attention to user setting of finalized, BUT it's
688     // still part of the wire packet -- so read & discard.
689     mFinalized = data.readInt32();
690     mFinalized = 1;
691     mTimestamp = data.readInt64();
692 
693     int count = data.readInt32();
694     for (int i = 0; i < count ; i++) {
695             MediaAnalyticsItem::Attr attr = data.readCString();
696             int32_t ztype = data.readInt32();
697                 switch (ztype) {
698                     case MediaAnalyticsItem::kTypeInt32:
699                             setInt32(attr, data.readInt32());
700                             break;
701                     case MediaAnalyticsItem::kTypeInt64:
702                             setInt64(attr, data.readInt64());
703                             break;
704                     case MediaAnalyticsItem::kTypeDouble:
705                             setDouble(attr, data.readDouble());
706                             break;
707                     case MediaAnalyticsItem::kTypeCString:
708                             setCString(attr, data.readCString());
709                             break;
710                     case MediaAnalyticsItem::kTypeRate:
711                             {
712                                 int64_t count = data.readInt64();
713                                 int64_t duration = data.readInt64();
714                                 setRate(attr, count, duration);
715                             }
716                             break;
717                     default:
718                             ALOGE("reading bad item type: %d, idx %d",
719                                   ztype, i);
720                             return -1;
721                 }
722     }
723 
724     return 0;
725 }
726 
writeToParcel(Parcel * data)727 int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) {
728 
729     if (data == NULL) return -1;
730 
731     int32_t version = 0;
732     data->writeInt32(version);
733 
734     switch(version) {
735         case 0:
736           return writeToParcel0(data);
737           break;
738         default:
739           ALOGE("Unsupported MediaAnalyticsItem Parcel version: %d", version);
740           return -1;
741     }
742 }
743 
writeToParcel0(Parcel * data)744 int32_t MediaAnalyticsItem::writeToParcel0(Parcel *data) {
745 
746     data->writeCString(mKey.c_str());
747     data->writeInt32(mPid);
748     data->writeInt32(mUid);
749     data->writeCString(mPkgName.c_str());
750     data->writeInt64(mPkgVersionCode);
751     data->writeInt64(mSessionID);
752     data->writeInt32(mFinalized);
753     data->writeInt64(mTimestamp);
754 
755     // set of items
756     int count = mPropCount;
757     data->writeInt32(count);
758     for (int i = 0 ; i < count; i++ ) {
759             Prop *prop = &mProps[i];
760             data->writeCString(prop->mName);
761             data->writeInt32(prop->mType);
762             switch (prop->mType) {
763                 case MediaAnalyticsItem::kTypeInt32:
764                         data->writeInt32(prop->u.int32Value);
765                         break;
766                 case MediaAnalyticsItem::kTypeInt64:
767                         data->writeInt64(prop->u.int64Value);
768                         break;
769                 case MediaAnalyticsItem::kTypeDouble:
770                         data->writeDouble(prop->u.doubleValue);
771                         break;
772                 case MediaAnalyticsItem::kTypeRate:
773                         data->writeInt64(prop->u.rate.count);
774                         data->writeInt64(prop->u.rate.duration);
775                         break;
776                 case MediaAnalyticsItem::kTypeCString:
777                         data->writeCString(prop->u.CStringValue);
778                         break;
779                 default:
780                         ALOGE("found bad Prop type: %d, idx %d, name %s",
781                               prop->mType, i, prop->mName);
782                         break;
783             }
784     }
785 
786     return 0;
787 }
788 
toCString()789 const char *MediaAnalyticsItem::toCString() {
790    return toCString(PROTO_LAST);
791 }
792 
toCString(int version)793 const char * MediaAnalyticsItem::toCString(int version) {
794     std::string val = toString(version);
795     return strdup(val.c_str());
796 }
797 
toString()798 std::string MediaAnalyticsItem::toString() {
799    return toString(PROTO_LAST);
800 }
801 
toString(int version)802 std::string MediaAnalyticsItem::toString(int version) {
803 
804     // v0 : released with 'o'
805     // v1 : bug fix (missing pid/finalized separator),
806     //      adds apk name, apk version code
807 
808     if (version <= PROTO_FIRST) {
809         // default to original v0 format, until proper parsers are in place
810         version = PROTO_V0;
811     } else if (version > PROTO_LAST) {
812         version = PROTO_LAST;
813     }
814 
815     std::string result;
816     char buffer[512];
817 
818     if (version == PROTO_V0) {
819         result = "(";
820     } else {
821         snprintf(buffer, sizeof(buffer), "[%d:", version);
822         result.append(buffer);
823     }
824 
825     // same order as we spill into the parcel, although not required
826     // key+session are our primary matching criteria
827     result.append(mKey.c_str());
828     result.append(":");
829     snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
830     result.append(buffer);
831 
832     snprintf(buffer, sizeof(buffer), "%d:", mUid);
833     result.append(buffer);
834 
835     if (version >= PROTO_V1) {
836         result.append(mPkgName);
837         snprintf(buffer, sizeof(buffer), ":%"  PRId64 ":", mPkgVersionCode);
838         result.append(buffer);
839     }
840 
841     // in 'o' (v1) , the separator between pid and finalized was omitted
842     if (version <= PROTO_V0) {
843         snprintf(buffer, sizeof(buffer), "%d", mPid);
844     } else {
845         snprintf(buffer, sizeof(buffer), "%d:", mPid);
846     }
847     result.append(buffer);
848 
849     snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
850     result.append(buffer);
851     snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mTimestamp);
852     result.append(buffer);
853 
854     // set of items
855     int count = mPropCount;
856     snprintf(buffer, sizeof(buffer), "%d:", count);
857     result.append(buffer);
858     for (int i = 0 ; i < count; i++ ) {
859             Prop *prop = &mProps[i];
860             switch (prop->mType) {
861                 case MediaAnalyticsItem::kTypeInt32:
862                         snprintf(buffer,sizeof(buffer),
863                         "%s=%d:", prop->mName, prop->u.int32Value);
864                         break;
865                 case MediaAnalyticsItem::kTypeInt64:
866                         snprintf(buffer,sizeof(buffer),
867                         "%s=%" PRId64 ":", prop->mName, prop->u.int64Value);
868                         break;
869                 case MediaAnalyticsItem::kTypeDouble:
870                         snprintf(buffer,sizeof(buffer),
871                         "%s=%e:", prop->mName, prop->u.doubleValue);
872                         break;
873                 case MediaAnalyticsItem::kTypeRate:
874                         snprintf(buffer,sizeof(buffer),
875                         "%s=%" PRId64 "/%" PRId64 ":", prop->mName,
876                         prop->u.rate.count, prop->u.rate.duration);
877                         break;
878                 case MediaAnalyticsItem::kTypeCString:
879                         snprintf(buffer,sizeof(buffer), "%s=", prop->mName);
880                         result.append(buffer);
881                         // XXX: sanitize string for ':' '='
882                         result.append(prop->u.CStringValue);
883                         buffer[0] = ':';
884                         buffer[1] = '\0';
885                         break;
886                 default:
887                         ALOGE("to_String bad item type: %d for %s",
888                               prop->mType, prop->mName);
889                         break;
890             }
891             result.append(buffer);
892     }
893 
894     if (version == PROTO_V0) {
895         result.append(")");
896     } else {
897         result.append("]");
898     }
899 
900     return result;
901 }
902 
903 // for the lazy, we offer methods that finds the service and
904 // calls the appropriate daemon
selfrecord()905 bool MediaAnalyticsItem::selfrecord() {
906     return selfrecord(false);
907 }
908 
selfrecord(bool forcenew)909 bool MediaAnalyticsItem::selfrecord(bool forcenew) {
910 
911     if (DEBUG_API) {
912         std::string p = this->toString();
913         ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
914     }
915 
916     sp<IMediaAnalyticsService> svc = getInstance();
917 
918     if (svc != NULL) {
919         MediaAnalyticsItem::SessionID_t newid = svc->submit(this, forcenew);
920         if (newid == SessionIDInvalid) {
921             std::string p = this->toString();
922             ALOGW("Failed to record: %s [forcenew=%d]", p.c_str(), forcenew);
923             return false;
924         }
925         return true;
926     } else {
927         return false;
928     }
929 }
930 
931 // get a connection we can reuse for most of our lifetime
932 // static
933 sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
934 static Mutex sInitMutex;
935 static int remainingBindAttempts = SVC_TRIES;
936 
937 //static
isEnabled()938 bool MediaAnalyticsItem::isEnabled() {
939     int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
940 
941     if (enabled == -1) {
942         enabled = property_get_int32(MediaAnalyticsItem::EnabledPropertyPersist, -1);
943     }
944     if (enabled == -1) {
945         enabled = MediaAnalyticsItem::EnabledProperty_default;
946     }
947     if (enabled <= 0) {
948         return false;
949     }
950     return true;
951 }
952 
953 
954 // monitor health of our connection to the metrics service
955 class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
binderDied(const wp<IBinder> &)956         virtual void binderDied(const wp<IBinder> &) {
957             ALOGW("Reacquire service connection on next request");
958             MediaAnalyticsItem::dropInstance();
959         }
960 };
961 
962 static sp<MediaMetricsDeathNotifier> sNotifier = NULL;
963 
964 // static
dropInstance()965 void MediaAnalyticsItem::dropInstance() {
966     Mutex::Autolock _l(sInitMutex);
967     remainingBindAttempts = SVC_TRIES;
968     sAnalyticsService = NULL;
969 }
970 
971 //static
getInstance()972 sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
973 
974     static const char *servicename = "media.metrics";
975     int enabled = isEnabled();
976 
977     if (enabled == false) {
978         if (DEBUG_SERVICEACCESS) {
979                 ALOGD("disabled");
980         }
981         return NULL;
982     }
983 
984     // completely skip logging from certain UIDs. We do this here
985     // to avoid the multi-second timeouts while we learn that
986     // sepolicy will not let us find the service.
987     // We do this only for a select set of UIDs
988     // The sepolicy protection is still in place, we just want a faster
989     // response from this specific, small set of uids.
990     {
991         uid_t uid = getuid();
992         switch (uid) {
993             case AID_RADIO:     // telephony subsystem, RIL
994                 return NULL;
995                 break;
996             default:
997                 // let sepolicy deny access if appropriate
998                 break;
999         }
1000     }
1001 
1002     {
1003         Mutex::Autolock _l(sInitMutex);
1004         const char *badness = "";
1005 
1006         // think of remainingBindAttempts as telling us whether service==NULL because
1007         // (1) we haven't tried to initialize it yet
1008         // (2) we've tried to initialize it, but failed.
1009         if (sAnalyticsService == NULL && remainingBindAttempts > 0) {
1010             sp<IServiceManager> sm = defaultServiceManager();
1011             if (sm != NULL) {
1012                 sp<IBinder> binder = sm->getService(String16(servicename));
1013                 if (binder != NULL) {
1014                     sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
1015                     if (sNotifier != NULL) {
1016                         sNotifier = NULL;
1017                     }
1018                     sNotifier = new MediaMetricsDeathNotifier();
1019                     binder->linkToDeath(sNotifier);
1020                 } else {
1021                     badness = "did not find service";
1022                 }
1023             } else {
1024                 badness = "No Service Manager access";
1025             }
1026 
1027             if (sAnalyticsService == NULL) {
1028                 if (remainingBindAttempts > 0) {
1029                     remainingBindAttempts--;
1030                 }
1031                 if (DEBUG_SERVICEACCESS) {
1032                     ALOGD("Unable to bind to service %s: %s", servicename, badness);
1033                 }
1034             }
1035         }
1036 
1037         return sAnalyticsService;
1038     }
1039 }
1040 
1041 // merge the info from 'incoming' into this record.
1042 // we finish with a union of this+incoming and special handling for collisions
merge(MediaAnalyticsItem * incoming)1043 bool MediaAnalyticsItem::merge(MediaAnalyticsItem *incoming) {
1044 
1045     // if I don't have key or session id, take them from incoming
1046     // 'this' should never be missing both of them...
1047     if (mKey.empty()) {
1048         mKey = incoming->mKey;
1049     } else if (mSessionID == 0) {
1050         mSessionID = incoming->mSessionID;
1051     }
1052 
1053     // for each attribute from 'incoming', resolve appropriately
1054     int nattr = incoming->mPropCount;
1055     for (int i = 0 ; i < nattr; i++ ) {
1056         Prop *iprop = &incoming->mProps[i];
1057         const char *p = iprop->mName;
1058         size_t len = strlen(p);
1059 
1060         // should ignore a zero length name...
1061         if (len == 0) {
1062             continue;
1063         }
1064 
1065         Prop *oprop = findProp(iprop->mName);
1066 
1067         if (oprop == NULL) {
1068             // no oprop, so we insert the new one
1069             oprop = allocateProp(p);
1070             if (oprop != NULL) {
1071                 copyProp(oprop, iprop);
1072             } else {
1073                 ALOGW("dropped property '%s'", iprop->mName);
1074             }
1075         } else {
1076             copyProp(oprop, iprop);
1077         }
1078     }
1079 
1080     // not sure when we'd return false...
1081     return true;
1082 }
1083 
1084 // a byte array; contents are
1085 // overall length (uint32) including the length field itself
1086 // encoding version (uint32)
1087 // count of properties (uint32)
1088 // N copies of:
1089 //     property name as length(int16), bytes
1090 //         the bytes WILL include the null terminator of the name
1091 //     type (uint8 -- 1 byte)
1092 //     size of value field (int16 -- 2 bytes)
1093 //     value (size based on type)
1094 //       int32, int64, double -- little endian 4/8/8 bytes respectively
1095 //       cstring -- N bytes of value [WITH terminator]
1096 
1097 enum { kInt32 = 0, kInt64, kDouble, kRate, kCString};
1098 
dumpAttributes(char ** pbuffer,size_t * plength)1099 bool MediaAnalyticsItem::dumpAttributes(char **pbuffer, size_t *plength) {
1100 
1101     char *build = NULL;
1102 
1103     if (pbuffer == NULL || plength == NULL)
1104         return false;
1105 
1106     // consistency for the caller, who owns whatever comes back in this pointer.
1107     *pbuffer = NULL;
1108 
1109     // first, let's calculate sizes
1110     int32_t goal = 0;
1111     int32_t version = 0;
1112 
1113     goal += sizeof(uint32_t);   // overall length, including the length field
1114     goal += sizeof(uint32_t);   // encoding version
1115     goal += sizeof(uint32_t);   // # properties
1116 
1117     int32_t count = mPropCount;
1118     for (int i = 0 ; i < count; i++ ) {
1119         Prop *prop = &mProps[i];
1120         goal += sizeof(uint16_t);           // name length
1121         goal += strlen(prop->mName) + 1;    // string + null
1122         goal += sizeof(uint8_t);            // type
1123         goal += sizeof(uint16_t);           // size of value
1124         switch (prop->mType) {
1125             case MediaAnalyticsItem::kTypeInt32:
1126                     goal += sizeof(uint32_t);
1127                     break;
1128             case MediaAnalyticsItem::kTypeInt64:
1129                     goal += sizeof(uint64_t);
1130                     break;
1131             case MediaAnalyticsItem::kTypeDouble:
1132                     goal += sizeof(double);
1133                     break;
1134             case MediaAnalyticsItem::kTypeRate:
1135                     goal += 2 * sizeof(uint64_t);
1136                     break;
1137             case MediaAnalyticsItem::kTypeCString:
1138                     // length + actual string + null
1139                     goal += strlen(prop->u.CStringValue) + 1;
1140                     break;
1141             default:
1142                     ALOGE("found bad Prop type: %d, idx %d, name %s",
1143                           prop->mType, i, prop->mName);
1144                     return false;
1145         }
1146     }
1147 
1148     // now that we have a size... let's allocate and fill
1149     build = (char *)malloc(goal);
1150     if (build == NULL)
1151         return false;
1152 
1153     memset(build, 0, goal);
1154 
1155     char *filling = build;
1156 
1157 #define _INSERT(val, size) \
1158     { memcpy(filling, &(val), (size)); filling += (size);}
1159 #define _INSERTSTRING(val, size) \
1160     { memcpy(filling, (val), (size)); filling += (size);}
1161 
1162     _INSERT(goal, sizeof(int32_t));
1163     _INSERT(version, sizeof(int32_t));
1164     _INSERT(count, sizeof(int32_t));
1165 
1166     for (int i = 0 ; i < count; i++ ) {
1167         Prop *prop = &mProps[i];
1168         int16_t attrNameLen = strlen(prop->mName) + 1;
1169         _INSERT(attrNameLen, sizeof(int16_t));
1170         _INSERTSTRING(prop->mName, attrNameLen);    // termination included
1171         int8_t elemtype;
1172         int16_t elemsize;
1173         switch (prop->mType) {
1174             case MediaAnalyticsItem::kTypeInt32:
1175                 {
1176                     elemtype = kInt32;
1177                     _INSERT(elemtype, sizeof(int8_t));
1178                     elemsize = sizeof(int32_t);
1179                     _INSERT(elemsize, sizeof(int16_t));
1180 
1181                     _INSERT(prop->u.int32Value, sizeof(int32_t));
1182                     break;
1183                 }
1184             case MediaAnalyticsItem::kTypeInt64:
1185                 {
1186                     elemtype = kInt64;
1187                     _INSERT(elemtype, sizeof(int8_t));
1188                     elemsize = sizeof(int64_t);
1189                     _INSERT(elemsize, sizeof(int16_t));
1190 
1191                     _INSERT(prop->u.int64Value, sizeof(int64_t));
1192                     break;
1193                 }
1194             case MediaAnalyticsItem::kTypeDouble:
1195                 {
1196                     elemtype = kDouble;
1197                     _INSERT(elemtype, sizeof(int8_t));
1198                     elemsize = sizeof(double);
1199                     _INSERT(elemsize, sizeof(int16_t));
1200 
1201                     _INSERT(prop->u.doubleValue, sizeof(double));
1202                     break;
1203                 }
1204             case MediaAnalyticsItem::kTypeRate:
1205                 {
1206                     elemtype = kRate;
1207                     _INSERT(elemtype, sizeof(int8_t));
1208                     elemsize = 2 * sizeof(uint64_t);
1209                     _INSERT(elemsize, sizeof(int16_t));
1210 
1211                     _INSERT(prop->u.rate.count, sizeof(uint64_t));
1212                     _INSERT(prop->u.rate.duration, sizeof(uint64_t));
1213                     break;
1214                 }
1215             case MediaAnalyticsItem::kTypeCString:
1216                 {
1217                     elemtype = kCString;
1218                     _INSERT(elemtype, sizeof(int8_t));
1219                     elemsize = strlen(prop->u.CStringValue) + 1;
1220                     _INSERT(elemsize, sizeof(int16_t));
1221 
1222                     _INSERTSTRING(prop->u.CStringValue, elemsize);
1223                     break;
1224                 }
1225             default:
1226                     // error if can't encode; warning if can't decode
1227                     ALOGE("found bad Prop type: %d, idx %d, name %s",
1228                           prop->mType, i, prop->mName);
1229                     goto badness;
1230         }
1231     }
1232 
1233     if (build + goal != filling) {
1234         ALOGE("problems populating; wrote=%d planned=%d",
1235               (int)(filling-build), goal);
1236         goto badness;
1237     }
1238 
1239     *pbuffer = build;
1240     *plength = goal;
1241 
1242     return true;
1243 
1244   badness:
1245     free(build);
1246     return false;
1247 }
1248 
1249 } // namespace android
1250 
1251