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