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