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 56 MediaAnalyticsItem *MediaAnalyticsItem::create() 57 { 58 return MediaAnalyticsItem::create(kKeyNone); 59 } 60 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 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 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 95 MediaAnalyticsItem::~MediaAnalyticsItem() { 96 if (DEBUG_ALLOCATIONS) { 97 ALOGD("Destroy MediaAnalyticsItem @ %p", this); 98 } 99 clear(); 100 } 101 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 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 151 MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) { 152 mSessionID = id; 153 return *this; 154 } 155 156 MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const { 157 return mSessionID; 158 } 159 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 175 MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() { 176 mSessionID = MediaAnalyticsItem::SessionIDNone; 177 return *this; 178 } 179 180 MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) { 181 mTimestamp = ts; 182 return *this; 183 } 184 185 nsecs_t MediaAnalyticsItem::getTimestamp() const { 186 return mTimestamp; 187 } 188 189 MediaAnalyticsItem &MediaAnalyticsItem::setPid(pid_t pid) { 190 mPid = pid; 191 return *this; 192 } 193 194 pid_t MediaAnalyticsItem::getPid() const { 195 return mPid; 196 } 197 198 MediaAnalyticsItem &MediaAnalyticsItem::setUid(uid_t uid) { 199 mUid = uid; 200 return *this; 201 } 202 203 uid_t MediaAnalyticsItem::getUid() const { 204 return mUid; 205 } 206 207 MediaAnalyticsItem &MediaAnalyticsItem::setPkgName(const std::string &pkgName) { 208 mPkgName = pkgName; 209 return *this; 210 } 211 212 MediaAnalyticsItem &MediaAnalyticsItem::setPkgVersionCode(int64_t pkgVersionCode) { 213 mPkgVersionCode = pkgVersionCode; 214 return *this; 215 } 216 217 int64_t MediaAnalyticsItem::getPkgVersionCode() const { 218 return mPkgVersionCode; 219 } 220 221 // this key is for the overall record -- "codec", "player", "drm", etc 222 MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) { 223 mKey = key; 224 return *this; 225 } 226 227 MediaAnalyticsItem::Key MediaAnalyticsItem::getKey() { 228 return mKey; 229 } 230 231 // number of attributes we have in this record 232 int32_t MediaAnalyticsItem::count() const { 233 return mPropCount; 234 } 235 236 // find the proper entry in the list 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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) 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 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 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 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 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 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 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 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 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 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 779 const char *MediaAnalyticsItem::toCString() { 780 return toCString(PROTO_LAST); 781 } 782 783 const char * MediaAnalyticsItem::toCString(int version) { 784 std::string val = toString(version); 785 return strdup(val.c_str()); 786 } 787 788 std::string MediaAnalyticsItem::toString() { 789 return toString(PROTO_LAST); 790 } 791 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 895 bool MediaAnalyticsItem::selfrecord() { 896 return selfrecord(false); 897 } 898 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 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 { 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 955 void MediaAnalyticsItem::dropInstance() { 956 Mutex::Autolock _l(sInitMutex); 957 remainingBindAttempts = SVC_TRIES; 958 sAnalyticsService = NULL; 959 } 960 961 //static 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 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 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