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 mPkgVersionCode(0),
63 mSessionID(MediaAnalyticsItem::SessionIDNone),
64 mTimestamp(0),
65 mFinalized(0),
66 mPropCount(0), mPropSize(0), mProps(NULL)
67 {
68 mKey = MediaAnalyticsItem::kKeyNone;
69 }
70
MediaAnalyticsItem(MediaAnalyticsItem::Key key)71 MediaAnalyticsItem::MediaAnalyticsItem(MediaAnalyticsItem::Key key)
72 : mPid(-1),
73 mUid(-1),
74 mPkgVersionCode(0),
75 mSessionID(MediaAnalyticsItem::SessionIDNone),
76 mTimestamp(0),
77 mFinalized(0),
78 mPropCount(0), mPropSize(0), mProps(NULL)
79 {
80 if (DEBUG_ALLOCATIONS) {
81 ALOGD("Allocate MediaAnalyticsItem @ %p", this);
82 }
83 mKey = key;
84 }
85
~MediaAnalyticsItem()86 MediaAnalyticsItem::~MediaAnalyticsItem() {
87 if (DEBUG_ALLOCATIONS) {
88 ALOGD("Destroy MediaAnalyticsItem @ %p", this);
89 }
90 clear();
91 }
92
clear()93 void MediaAnalyticsItem::clear() {
94
95 // clean allocated storage from key
96 mKey.clear();
97
98 // clean various major parameters
99 mSessionID = MediaAnalyticsItem::SessionIDNone;
100
101 // clean attributes
102 // contents of the attributes
103 for (size_t i = 0 ; i < mPropCount; i++ ) {
104 clearProp(&mProps[i]);
105 }
106 // the attribute records themselves
107 if (mProps != NULL) {
108 free(mProps);
109 mProps = NULL;
110 }
111 mPropSize = 0;
112 mPropCount = 0;
113
114 return;
115 }
116
117 // make a deep copy of myself
dup()118 MediaAnalyticsItem *MediaAnalyticsItem::dup() {
119 MediaAnalyticsItem *dst = new MediaAnalyticsItem(this->mKey);
120
121 if (dst != NULL) {
122 // key as part of constructor
123 dst->mPid = this->mPid;
124 dst->mUid = this->mUid;
125 dst->mPkgName = this->mPkgName;
126 dst->mPkgVersionCode = this->mPkgVersionCode;
127 dst->mSessionID = this->mSessionID;
128 dst->mTimestamp = this->mTimestamp;
129 dst->mFinalized = this->mFinalized;
130
131 // properties aka attributes
132 dst->growProps(this->mPropCount);
133 for(size_t i=0;i<mPropCount;i++) {
134 copyProp(&dst->mProps[i], &this->mProps[i]);
135 }
136 dst->mPropCount = this->mPropCount;
137 }
138
139 return dst;
140 }
141
142 // so clients can send intermediate values to be overlaid later
setFinalized(bool value)143 MediaAnalyticsItem &MediaAnalyticsItem::setFinalized(bool value) {
144 mFinalized = value;
145 return *this;
146 }
147
getFinalized() const148 bool MediaAnalyticsItem::getFinalized() const {
149 return mFinalized;
150 }
151
setSessionID(MediaAnalyticsItem::SessionID_t id)152 MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
153 mSessionID = id;
154 return *this;
155 }
156
getSessionID() const157 MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const {
158 return mSessionID;
159 }
160
generateSessionID()161 MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::generateSessionID() {
162
163 if (mSessionID == SessionIDNone) {
164 // get one from the server
165 MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
166 sp<IMediaAnalyticsService> svc = getInstance();
167 if (svc != NULL) {
168 newid = svc->generateUniqueSessionID();
169 }
170 mSessionID = newid;
171 }
172
173 return mSessionID;
174 }
175
clearSessionID()176 MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() {
177 mSessionID = MediaAnalyticsItem::SessionIDNone;
178 return *this;
179 }
180
setTimestamp(nsecs_t ts)181 MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) {
182 mTimestamp = ts;
183 return *this;
184 }
185
getTimestamp() const186 nsecs_t MediaAnalyticsItem::getTimestamp() const {
187 return mTimestamp;
188 }
189
setPid(pid_t pid)190 MediaAnalyticsItem &MediaAnalyticsItem::setPid(pid_t pid) {
191 mPid = pid;
192 return *this;
193 }
194
getPid() const195 pid_t MediaAnalyticsItem::getPid() const {
196 return mPid;
197 }
198
setUid(uid_t uid)199 MediaAnalyticsItem &MediaAnalyticsItem::setUid(uid_t uid) {
200 mUid = uid;
201 return *this;
202 }
203
getUid() const204 uid_t MediaAnalyticsItem::getUid() const {
205 return mUid;
206 }
207
setPkgName(AString pkgName)208 MediaAnalyticsItem &MediaAnalyticsItem::setPkgName(AString pkgName) {
209 mPkgName = pkgName;
210 return *this;
211 }
212
getPkgName() const213 AString MediaAnalyticsItem::getPkgName() const {
214 return mPkgName;
215 }
216
setPkgVersionCode(int32_t pkgVersionCode)217 MediaAnalyticsItem &MediaAnalyticsItem::setPkgVersionCode(int32_t pkgVersionCode) {
218 mPkgVersionCode = pkgVersionCode;
219 return *this;
220 }
221
getPkgVersionCode() const222 int32_t MediaAnalyticsItem::getPkgVersionCode() const {
223 return mPkgVersionCode;
224 }
225
226 // this key is for the overall record -- "codec", "player", "drm", etc
setKey(MediaAnalyticsItem::Key key)227 MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
228 mKey = key;
229 return *this;
230 }
231
getKey()232 MediaAnalyticsItem::Key MediaAnalyticsItem::getKey() {
233 return mKey;
234 }
235
236 // number of attributes we have in this record
count() const237 int32_t MediaAnalyticsItem::count() const {
238 return mPropCount;
239 }
240
241 // find the proper entry in the list
findPropIndex(const char * name,size_t len)242 size_t MediaAnalyticsItem::findPropIndex(const char *name, size_t len)
243 {
244 size_t i = 0;
245 for (; i < mPropCount; i++) {
246 Prop *prop = &mProps[i];
247 if (prop->mNameLen != len) {
248 continue;
249 }
250 if (memcmp(name, prop->mName, len) == 0) {
251 break;
252 }
253 }
254 return i;
255 }
256
findProp(const char * name)257 MediaAnalyticsItem::Prop *MediaAnalyticsItem::findProp(const char *name) {
258 size_t len = strlen(name);
259 size_t i = findPropIndex(name, len);
260 if (i < mPropCount) {
261 return &mProps[i];
262 }
263 return NULL;
264 }
265
setName(const char * name,size_t len)266 void MediaAnalyticsItem::Prop::setName(const char *name, size_t len) {
267 mNameLen = len;
268 mName = (const char *) malloc(len+1);
269 memcpy ((void *)mName, name, len+1);
270 }
271
272 // used only as part of a storing operation
allocateProp(const char * name)273 MediaAnalyticsItem::Prop *MediaAnalyticsItem::allocateProp(const char *name) {
274 size_t len = strlen(name);
275 size_t i = findPropIndex(name, len);
276 Prop *prop;
277
278 if (i < mPropCount) {
279 prop = &mProps[i];
280 } else {
281 if (i == mPropSize) {
282 growProps();
283 // XXX: verify success
284 }
285 i = mPropCount++;
286 prop = &mProps[i];
287 prop->setName(name, len);
288 prop->mType = kTypeNone; // make caller set type info
289 }
290
291 return prop;
292 }
293
294 // used within the summarizers; return whether property existed
removeProp(const char * name)295 bool MediaAnalyticsItem::removeProp(const char *name) {
296 size_t len = strlen(name);
297 size_t i = findPropIndex(name, len);
298 if (i < mPropCount) {
299 Prop *prop = &mProps[i];
300 clearProp(prop);
301 if (i != mPropCount-1) {
302 // in the middle, bring last one down to fill gap
303 copyProp(prop, &mProps[mPropCount-1]);
304 clearProp(&mProps[mPropCount-1]);
305 }
306 mPropCount--;
307 return true;
308 }
309 return false;
310 }
311
312 // set the values
setInt32(MediaAnalyticsItem::Attr name,int32_t value)313 void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
314 Prop *prop = allocateProp(name);
315 prop->mType = kTypeInt32;
316 prop->u.int32Value = value;
317 }
318
setInt64(MediaAnalyticsItem::Attr name,int64_t value)319 void MediaAnalyticsItem::setInt64(MediaAnalyticsItem::Attr name, int64_t value) {
320 Prop *prop = allocateProp(name);
321 prop->mType = kTypeInt64;
322 prop->u.int64Value = value;
323 }
324
setDouble(MediaAnalyticsItem::Attr name,double value)325 void MediaAnalyticsItem::setDouble(MediaAnalyticsItem::Attr name, double value) {
326 Prop *prop = allocateProp(name);
327 prop->mType = kTypeDouble;
328 prop->u.doubleValue = value;
329 }
330
setCString(MediaAnalyticsItem::Attr name,const char * value)331 void MediaAnalyticsItem::setCString(MediaAnalyticsItem::Attr name, const char *value) {
332
333 Prop *prop = allocateProp(name);
334 // any old value will be gone
335 prop->mType = kTypeCString;
336 prop->u.CStringValue = strdup(value);
337 }
338
setRate(MediaAnalyticsItem::Attr name,int64_t count,int64_t duration)339 void MediaAnalyticsItem::setRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
340 Prop *prop = allocateProp(name);
341 prop->mType = kTypeRate;
342 prop->u.rate.count = count;
343 prop->u.rate.duration = duration;
344 }
345
346
347 // find/add/set fused into a single operation
addInt32(MediaAnalyticsItem::Attr name,int32_t value)348 void MediaAnalyticsItem::addInt32(MediaAnalyticsItem::Attr name, int32_t value) {
349 Prop *prop = allocateProp(name);
350 switch (prop->mType) {
351 case kTypeInt32:
352 prop->u.int32Value += value;
353 break;
354 default:
355 clearPropValue(prop);
356 prop->mType = kTypeInt32;
357 prop->u.int32Value = value;
358 break;
359 }
360 }
361
addInt64(MediaAnalyticsItem::Attr name,int64_t value)362 void MediaAnalyticsItem::addInt64(MediaAnalyticsItem::Attr name, int64_t value) {
363 Prop *prop = allocateProp(name);
364 switch (prop->mType) {
365 case kTypeInt64:
366 prop->u.int64Value += value;
367 break;
368 default:
369 clearPropValue(prop);
370 prop->mType = kTypeInt64;
371 prop->u.int64Value = value;
372 break;
373 }
374 }
375
addRate(MediaAnalyticsItem::Attr name,int64_t count,int64_t duration)376 void MediaAnalyticsItem::addRate(MediaAnalyticsItem::Attr name, int64_t count, int64_t duration) {
377 Prop *prop = allocateProp(name);
378 switch (prop->mType) {
379 case kTypeRate:
380 prop->u.rate.count += count;
381 prop->u.rate.duration += duration;
382 break;
383 default:
384 clearPropValue(prop);
385 prop->mType = kTypeRate;
386 prop->u.rate.count = count;
387 prop->u.rate.duration = duration;
388 break;
389 }
390 }
391
addDouble(MediaAnalyticsItem::Attr name,double value)392 void MediaAnalyticsItem::addDouble(MediaAnalyticsItem::Attr name, double value) {
393 Prop *prop = allocateProp(name);
394 switch (prop->mType) {
395 case kTypeDouble:
396 prop->u.doubleValue += value;
397 break;
398 default:
399 clearPropValue(prop);
400 prop->mType = kTypeDouble;
401 prop->u.doubleValue = value;
402 break;
403 }
404 }
405
406 // find & extract values
getInt32(MediaAnalyticsItem::Attr name,int32_t * value)407 bool MediaAnalyticsItem::getInt32(MediaAnalyticsItem::Attr name, int32_t *value) {
408 Prop *prop = findProp(name);
409 if (prop == NULL || prop->mType != kTypeInt32) {
410 return false;
411 }
412 if (value != NULL) {
413 *value = prop->u.int32Value;
414 }
415 return true;
416 }
417
getInt64(MediaAnalyticsItem::Attr name,int64_t * value)418 bool MediaAnalyticsItem::getInt64(MediaAnalyticsItem::Attr name, int64_t *value) {
419 Prop *prop = findProp(name);
420 if (prop == NULL || prop->mType != kTypeInt64) {
421 return false;
422 }
423 if (value != NULL) {
424 *value = prop->u.int64Value;
425 }
426 return true;
427 }
428
getRate(MediaAnalyticsItem::Attr name,int64_t * count,int64_t * duration,double * rate)429 bool MediaAnalyticsItem::getRate(MediaAnalyticsItem::Attr name, int64_t *count, int64_t *duration, double *rate) {
430 Prop *prop = findProp(name);
431 if (prop == NULL || prop->mType != kTypeRate) {
432 return false;
433 }
434 if (count != NULL) {
435 *count = prop->u.rate.count;
436 }
437 if (duration != NULL) {
438 *duration = prop->u.rate.duration;
439 }
440 if (rate != NULL) {
441 double r = 0.0;
442 if (prop->u.rate.duration != 0) {
443 r = prop->u.rate.count / (double) prop->u.rate.duration;
444 }
445 *rate = r;
446 }
447 return true;
448 }
449
getDouble(MediaAnalyticsItem::Attr name,double * value)450 bool MediaAnalyticsItem::getDouble(MediaAnalyticsItem::Attr name, double *value) {
451 Prop *prop = findProp(name);
452 if (prop == NULL || prop->mType != kTypeDouble) {
453 return false;
454 }
455 if (value != NULL) {
456 *value = prop->u.doubleValue;
457 }
458 return true;
459 }
460
461 // caller responsible for the returned string
getCString(MediaAnalyticsItem::Attr name,char ** value)462 bool MediaAnalyticsItem::getCString(MediaAnalyticsItem::Attr name, char **value) {
463 Prop *prop = findProp(name);
464 if (prop == NULL || prop->mType != kTypeDouble) {
465 return false;
466 }
467 if (value != NULL) {
468 *value = strdup(prop->u.CStringValue);
469 }
470 return true;
471 }
472
473 // remove indicated keys and their values
474 // return value is # keys removed
filter(int n,MediaAnalyticsItem::Attr attrs[])475 int32_t MediaAnalyticsItem::filter(int n, MediaAnalyticsItem::Attr attrs[]) {
476 int zapped = 0;
477 if (attrs == NULL || n <= 0) {
478 return -1;
479 }
480 for (ssize_t i = 0 ; i < n ; i++) {
481 const char *name = attrs[i];
482 size_t len = strlen(name);
483 size_t j = findPropIndex(name, len);
484 if (j >= mPropCount) {
485 // not there
486 continue;
487 } else if (j+1 == mPropCount) {
488 // last one, shorten
489 zapped++;
490 clearProp(&mProps[j]);
491 mPropCount--;
492 } else {
493 // in the middle, bring last one down and shorten
494 zapped++;
495 clearProp(&mProps[j]);
496 mProps[j] = mProps[mPropCount-1];
497 mPropCount--;
498 }
499 }
500 return zapped;
501 }
502
503 // remove any keys NOT in the provided list
504 // return value is # keys removed
filterNot(int n,MediaAnalyticsItem::Attr attrs[])505 int32_t MediaAnalyticsItem::filterNot(int n, MediaAnalyticsItem::Attr attrs[]) {
506 int zapped = 0;
507 if (attrs == NULL || n <= 0) {
508 return -1;
509 }
510 for (ssize_t i = mPropCount-1 ; i >=0 ; i--) {
511 Prop *prop = &mProps[i];
512 for (ssize_t j = 0; j < n ; j++) {
513 if (strcmp(prop->mName, attrs[j]) == 0) {
514 clearProp(prop);
515 zapped++;
516 if (i != (ssize_t)(mPropCount-1)) {
517 *prop = mProps[mPropCount-1];
518 }
519 initProp(&mProps[mPropCount-1]);
520 mPropCount--;
521 break;
522 }
523 }
524 }
525 return zapped;
526 }
527
528 // remove a single key
529 // return value is 0 (not found) or 1 (found and removed)
filter(MediaAnalyticsItem::Attr name)530 int32_t MediaAnalyticsItem::filter(MediaAnalyticsItem::Attr name) {
531 return filter(1, &name);
532 }
533
534 // handle individual items/properties stored within the class
535 //
536
initProp(Prop * prop)537 void MediaAnalyticsItem::initProp(Prop *prop) {
538 if (prop != NULL) {
539 prop->mName = NULL;
540 prop->mNameLen = 0;
541
542 prop->mType = kTypeNone;
543 }
544 }
545
clearProp(Prop * prop)546 void MediaAnalyticsItem::clearProp(Prop *prop)
547 {
548 if (prop != NULL) {
549 if (prop->mName != NULL) {
550 free((void *)prop->mName);
551 prop->mName = NULL;
552 prop->mNameLen = 0;
553 }
554
555 clearPropValue(prop);
556 }
557 }
558
clearPropValue(Prop * prop)559 void MediaAnalyticsItem::clearPropValue(Prop *prop)
560 {
561 if (prop != NULL) {
562 if (prop->mType == kTypeCString && prop->u.CStringValue != NULL) {
563 free(prop->u.CStringValue);
564 prop->u.CStringValue = NULL;
565 }
566 prop->mType = kTypeNone;
567 }
568 }
569
copyProp(Prop * dst,const Prop * src)570 void MediaAnalyticsItem::copyProp(Prop *dst, const Prop *src)
571 {
572 // get rid of any pointers in the dst
573 clearProp(dst);
574
575 *dst = *src;
576
577 // fix any pointers that we blindly copied, so we have our own copies
578 if (dst->mName) {
579 void *p = malloc(dst->mNameLen + 1);
580 memcpy (p, src->mName, dst->mNameLen + 1);
581 dst->mName = (const char *) p;
582 }
583 if (dst->mType == kTypeCString) {
584 dst->u.CStringValue = strdup(src->u.CStringValue);
585 }
586 }
587
growProps(int increment)588 void MediaAnalyticsItem::growProps(int increment)
589 {
590 if (increment <= 0) {
591 increment = kGrowProps;
592 }
593 int nsize = mPropSize + increment;
594 Prop *ni = (Prop *)realloc(mProps, sizeof(Prop) * nsize);
595
596 if (ni != NULL) {
597 for (int i = mPropSize; i < nsize; i++) {
598 initProp(&ni[i]);
599 }
600 mProps = ni;
601 mPropSize = nsize;
602 }
603 }
604
605 // Parcel / serialize things for binder calls
606 //
607
readFromParcel(const Parcel & data)608 int32_t MediaAnalyticsItem::readFromParcel(const Parcel& data) {
609 // into 'this' object
610 // .. we make a copy of the string to put away.
611 mKey = data.readCString();
612 mPid = data.readInt32();
613 mUid = data.readInt32();
614 mPkgName = data.readCString();
615 mPkgVersionCode = data.readInt32();
616 mSessionID = data.readInt64();
617 mFinalized = data.readInt32();
618 mTimestamp = data.readInt64();
619
620 int count = data.readInt32();
621 for (int i = 0; i < count ; i++) {
622 MediaAnalyticsItem::Attr attr = data.readCString();
623 int32_t ztype = data.readInt32();
624 switch (ztype) {
625 case MediaAnalyticsItem::kTypeInt32:
626 setInt32(attr, data.readInt32());
627 break;
628 case MediaAnalyticsItem::kTypeInt64:
629 setInt64(attr, data.readInt64());
630 break;
631 case MediaAnalyticsItem::kTypeDouble:
632 setDouble(attr, data.readDouble());
633 break;
634 case MediaAnalyticsItem::kTypeCString:
635 setCString(attr, data.readCString());
636 break;
637 case MediaAnalyticsItem::kTypeRate:
638 {
639 int64_t count = data.readInt64();
640 int64_t duration = data.readInt64();
641 setRate(attr, count, duration);
642 }
643 break;
644 default:
645 ALOGE("reading bad item type: %d, idx %d",
646 ztype, i);
647 return -1;
648 }
649 }
650
651 return 0;
652 }
653
writeToParcel(Parcel * data)654 int32_t MediaAnalyticsItem::writeToParcel(Parcel *data) {
655 if (data == NULL) return -1;
656
657
658 data->writeCString(mKey.c_str());
659 data->writeInt32(mPid);
660 data->writeInt32(mUid);
661 data->writeCString(mPkgName.c_str());
662 data->writeInt32(mPkgVersionCode);
663 data->writeInt64(mSessionID);
664 data->writeInt32(mFinalized);
665 data->writeInt64(mTimestamp);
666
667 // set of items
668 int count = mPropCount;
669 data->writeInt32(count);
670 for (int i = 0 ; i < count; i++ ) {
671 Prop *prop = &mProps[i];
672 data->writeCString(prop->mName);
673 data->writeInt32(prop->mType);
674 switch (prop->mType) {
675 case MediaAnalyticsItem::kTypeInt32:
676 data->writeInt32(prop->u.int32Value);
677 break;
678 case MediaAnalyticsItem::kTypeInt64:
679 data->writeInt64(prop->u.int64Value);
680 break;
681 case MediaAnalyticsItem::kTypeDouble:
682 data->writeDouble(prop->u.doubleValue);
683 break;
684 case MediaAnalyticsItem::kTypeRate:
685 data->writeInt64(prop->u.rate.count);
686 data->writeInt64(prop->u.rate.duration);
687 break;
688 case MediaAnalyticsItem::kTypeCString:
689 data->writeCString(prop->u.CStringValue);
690 break;
691 default:
692 ALOGE("found bad Prop type: %d, idx %d, name %s",
693 prop->mType, i, prop->mName);
694 break;
695 }
696 }
697
698 return 0;
699 }
700
701
toString()702 AString MediaAnalyticsItem::toString() {
703 return toString(-1);
704 }
705
toString(int version)706 AString MediaAnalyticsItem::toString(int version) {
707
708 // v0 : released with 'o'
709 // v1 : bug fix (missing pid/finalized separator),
710 // adds apk name, apk version code
711
712 if (version <= PROTO_FIRST) {
713 // default to original v0 format, until proper parsers are in place
714 version = PROTO_V0;
715 } else if (version > PROTO_LAST) {
716 version = PROTO_LAST;
717 }
718
719 AString result;
720 char buffer[512];
721
722 if (version == PROTO_V0) {
723 result = "(";
724 } else {
725 snprintf(buffer, sizeof(buffer), "[%d:", version);
726 result.append(buffer);
727 }
728
729 // same order as we spill into the parcel, although not required
730 // key+session are our primary matching criteria
731 result.append(mKey.c_str());
732 result.append(":");
733 snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
734 result.append(buffer);
735
736 snprintf(buffer, sizeof(buffer), "%d:", mUid);
737 result.append(buffer);
738
739 if (version >= PROTO_V1) {
740 result.append(mPkgName);
741 snprintf(buffer, sizeof(buffer), ":%d:", mPkgVersionCode);
742 result.append(buffer);
743 }
744
745 // in 'o' (v1) , the separator between pid and finalized was omitted
746 if (version <= PROTO_V0) {
747 snprintf(buffer, sizeof(buffer), "%d", mPid);
748 } else {
749 snprintf(buffer, sizeof(buffer), "%d:", mPid);
750 }
751 result.append(buffer);
752
753 snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
754 result.append(buffer);
755 snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mTimestamp);
756 result.append(buffer);
757
758 // set of items
759 int count = mPropCount;
760 snprintf(buffer, sizeof(buffer), "%d:", count);
761 result.append(buffer);
762 for (int i = 0 ; i < count; i++ ) {
763 Prop *prop = &mProps[i];
764 switch (prop->mType) {
765 case MediaAnalyticsItem::kTypeInt32:
766 snprintf(buffer,sizeof(buffer),
767 "%s=%d:", prop->mName, prop->u.int32Value);
768 break;
769 case MediaAnalyticsItem::kTypeInt64:
770 snprintf(buffer,sizeof(buffer),
771 "%s=%" PRId64 ":", prop->mName, prop->u.int64Value);
772 break;
773 case MediaAnalyticsItem::kTypeDouble:
774 snprintf(buffer,sizeof(buffer),
775 "%s=%e:", prop->mName, prop->u.doubleValue);
776 break;
777 case MediaAnalyticsItem::kTypeRate:
778 snprintf(buffer,sizeof(buffer),
779 "%s=%" PRId64 "/%" PRId64 ":", prop->mName,
780 prop->u.rate.count, prop->u.rate.duration);
781 break;
782 case MediaAnalyticsItem::kTypeCString:
783 snprintf(buffer,sizeof(buffer), "%s=", prop->mName);
784 result.append(buffer);
785 // XXX: sanitize string for ':' '='
786 result.append(prop->u.CStringValue);
787 buffer[0] = ':';
788 buffer[1] = '\0';
789 break;
790 default:
791 ALOGE("to_String bad item type: %d for %s",
792 prop->mType, prop->mName);
793 break;
794 }
795 result.append(buffer);
796 }
797
798 if (version == PROTO_V0) {
799 result.append(")");
800 } else {
801 result.append("]");
802 }
803
804 return result;
805 }
806
807 // for the lazy, we offer methods that finds the service and
808 // calls the appropriate daemon
selfrecord()809 bool MediaAnalyticsItem::selfrecord() {
810 return selfrecord(false);
811 }
812
selfrecord(bool forcenew)813 bool MediaAnalyticsItem::selfrecord(bool forcenew) {
814
815 if (DEBUG_API) {
816 AString p = this->toString();
817 ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
818 }
819
820 sp<IMediaAnalyticsService> svc = getInstance();
821
822 if (svc != NULL) {
823 MediaAnalyticsItem::SessionID_t newid = svc->submit(this, forcenew);
824 if (newid == SessionIDInvalid) {
825 AString p = this->toString();
826 ALOGW("Failed to record: %s [forcenew=%d]", p.c_str(), forcenew);
827 return false;
828 }
829 return true;
830 } else {
831 AString p = this->toString();
832 ALOGW("Unable to record: %s [forcenew=%d]", p.c_str(), forcenew);
833 return false;
834 }
835 }
836
837 // get a connection we can reuse for most of our lifetime
838 // static
839 sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
840 static Mutex sInitMutex;
841 static int remainingBindAttempts = SVC_TRIES;
842
843 //static
isEnabled()844 bool MediaAnalyticsItem::isEnabled() {
845 int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
846
847 if (enabled == -1) {
848 enabled = property_get_int32(MediaAnalyticsItem::EnabledPropertyPersist, -1);
849 }
850 if (enabled == -1) {
851 enabled = MediaAnalyticsItem::EnabledProperty_default;
852 }
853 if (enabled <= 0) {
854 return false;
855 }
856 return true;
857 }
858
859
860 // monitor health of our connection to the metrics service
861 class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
binderDied(const wp<IBinder> &)862 virtual void binderDied(const wp<IBinder> &) {
863 ALOGW("Reacquire service connection on next request");
864 MediaAnalyticsItem::dropInstance();
865 }
866 };
867
868 static sp<MediaMetricsDeathNotifier> sNotifier = NULL;
869
870 // static
dropInstance()871 void MediaAnalyticsItem::dropInstance() {
872 Mutex::Autolock _l(sInitMutex);
873 remainingBindAttempts = SVC_TRIES;
874 sAnalyticsService = NULL;
875 }
876
877 //static
getInstance()878 sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
879
880 static const char *servicename = "media.metrics";
881 int enabled = isEnabled();
882
883 if (enabled == false) {
884 if (DEBUG_SERVICEACCESS) {
885 ALOGD("disabled");
886 }
887 return NULL;
888 }
889
890 // completely skip logging from certain UIDs. We do this here
891 // to avoid the multi-second timeouts while we learn that
892 // sepolicy will not let us find the service.
893 // We do this only for a select set of UIDs
894 // The sepolicy protection is still in place, we just want a faster
895 // response from this specific, small set of uids.
896 {
897 uid_t uid = getuid();
898 switch (uid) {
899 case AID_RADIO: // telephony subsystem, RIL
900 return NULL;
901 break;
902 default:
903 // let sepolicy deny access if appropriate
904 break;
905 }
906 }
907
908 {
909 Mutex::Autolock _l(sInitMutex);
910 const char *badness = "";
911
912 // think of remainingBindAttempts as telling us whether service==NULL because
913 // (1) we haven't tried to initialize it yet
914 // (2) we've tried to initialize it, but failed.
915 if (sAnalyticsService == NULL && remainingBindAttempts > 0) {
916 sp<IServiceManager> sm = defaultServiceManager();
917 if (sm != NULL) {
918 sp<IBinder> binder = sm->getService(String16(servicename));
919 if (binder != NULL) {
920 sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
921 if (sNotifier != NULL) {
922 sNotifier = NULL;
923 }
924 sNotifier = new MediaMetricsDeathNotifier();
925 binder->linkToDeath(sNotifier);
926 } else {
927 badness = "did not find service";
928 }
929 } else {
930 badness = "No Service Manager access";
931 }
932
933 if (sAnalyticsService == NULL) {
934 if (remainingBindAttempts > 0) {
935 remainingBindAttempts--;
936 }
937 if (DEBUG_SERVICEACCESS) {
938 ALOGD("Unable to bind to service %s: %s", servicename, badness);
939 }
940 }
941 }
942
943 return sAnalyticsService;
944 }
945 }
946
947 // merge the info from 'incoming' into this record.
948 // we finish with a union of this+incoming and special handling for collisions
merge(MediaAnalyticsItem * incoming)949 bool MediaAnalyticsItem::merge(MediaAnalyticsItem *incoming) {
950
951 // if I don't have key or session id, take them from incoming
952 // 'this' should never be missing both of them...
953 if (mKey.empty()) {
954 mKey = incoming->mKey;
955 } else if (mSessionID == 0) {
956 mSessionID = incoming->mSessionID;
957 }
958
959 // we always take the more recent 'finalized' value
960 setFinalized(incoming->getFinalized());
961
962 // for each attribute from 'incoming', resolve appropriately
963 int nattr = incoming->mPropCount;
964 for (int i = 0 ; i < nattr; i++ ) {
965 Prop *iprop = &incoming->mProps[i];
966 Prop *oprop = findProp(iprop->mName);
967 const char *p = iprop->mName;
968 size_t len = strlen(p);
969 char semantic = p[len-1];
970
971 if (oprop == NULL) {
972 // no oprop, so we insert the new one
973 oprop = allocateProp(p);
974 copyProp(oprop, iprop);
975 } else {
976 // merge iprop into oprop
977 switch (semantic) {
978 case '<': // first aka keep old)
979 /* nop */
980 break;
981
982 default: // default is 'last'
983 case '>': // last (aka keep new)
984 copyProp(oprop, iprop);
985 break;
986
987 case '+': /* sum */
988 // XXX validate numeric types, sum in place
989 break;
990
991 }
992 }
993 }
994
995 // not sure when we'd return false...
996 return true;
997 }
998
999 } // namespace android
1000
1001