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