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