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 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 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 86 MediaAnalyticsItem::~MediaAnalyticsItem() { 87 if (DEBUG_ALLOCATIONS) { 88 ALOGD("Destroy MediaAnalyticsItem @ %p", this); 89 } 90 clear(); 91 } 92 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 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 143 MediaAnalyticsItem &MediaAnalyticsItem::setFinalized(bool value) { 144 mFinalized = value; 145 return *this; 146 } 147 148 bool MediaAnalyticsItem::getFinalized() const { 149 return mFinalized; 150 } 151 152 MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) { 153 mSessionID = id; 154 return *this; 155 } 156 157 MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const { 158 return mSessionID; 159 } 160 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 176 MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() { 177 mSessionID = MediaAnalyticsItem::SessionIDNone; 178 return *this; 179 } 180 181 MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) { 182 mTimestamp = ts; 183 return *this; 184 } 185 186 nsecs_t MediaAnalyticsItem::getTimestamp() const { 187 return mTimestamp; 188 } 189 190 MediaAnalyticsItem &MediaAnalyticsItem::setPid(pid_t pid) { 191 mPid = pid; 192 return *this; 193 } 194 195 pid_t MediaAnalyticsItem::getPid() const { 196 return mPid; 197 } 198 199 MediaAnalyticsItem &MediaAnalyticsItem::setUid(uid_t uid) { 200 mUid = uid; 201 return *this; 202 } 203 204 uid_t MediaAnalyticsItem::getUid() const { 205 return mUid; 206 } 207 208 MediaAnalyticsItem &MediaAnalyticsItem::setPkgName(AString pkgName) { 209 mPkgName = pkgName; 210 return *this; 211 } 212 213 AString MediaAnalyticsItem::getPkgName() const { 214 return mPkgName; 215 } 216 217 MediaAnalyticsItem &MediaAnalyticsItem::setPkgVersionCode(int32_t pkgVersionCode) { 218 mPkgVersionCode = pkgVersionCode; 219 return *this; 220 } 221 222 int32_t MediaAnalyticsItem::getPkgVersionCode() const { 223 return mPkgVersionCode; 224 } 225 226 // this key is for the overall record -- "codec", "player", "drm", etc 227 MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) { 228 mKey = key; 229 return *this; 230 } 231 232 MediaAnalyticsItem::Key MediaAnalyticsItem::getKey() { 233 return mKey; 234 } 235 236 // number of attributes we have in this record 237 int32_t MediaAnalyticsItem::count() const { 238 return mPropCount; 239 } 240 241 // find the proper entry in the list 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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) 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 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 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 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 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 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 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 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 702 AString MediaAnalyticsItem::toString() { 703 return toString(-1); 704 } 705 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 809 bool MediaAnalyticsItem::selfrecord() { 810 return selfrecord(false); 811 } 812 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 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 { 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 871 void MediaAnalyticsItem::dropInstance() { 872 Mutex::Autolock _l(sInitMutex); 873 remainingBindAttempts = SVC_TRIES; 874 sAnalyticsService = NULL; 875 } 876 877 //static 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 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