1/* 2 * Copyright (C) 2017 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// Proxy for media player implementations 18 19//#define LOG_NDEBUG 0 20#define LOG_TAG "MediaAnalyticsService" 21#include <utils/Log.h> 22 23#include <stdint.h> 24#include <inttypes.h> 25#include <sys/types.h> 26#include <sys/stat.h> 27#include <sys/time.h> 28#include <dirent.h> 29#include <unistd.h> 30 31#include <string.h> 32 33#include <cutils/atomic.h> 34#include <cutils/properties.h> // for property_get 35 36#include <utils/misc.h> 37 38#include <binder/IPCThreadState.h> 39#include <binder/IServiceManager.h> 40#include <binder/MemoryHeapBase.h> 41#include <binder/MemoryBase.h> 42#include <gui/Surface.h> 43#include <utils/Errors.h> // for status_t 44#include <utils/List.h> 45#include <utils/String8.h> 46#include <utils/SystemClock.h> 47#include <utils/Timers.h> 48#include <utils/Vector.h> 49 50#include <media/AudioPolicyHelper.h> 51#include <media/IMediaHTTPService.h> 52#include <media/IRemoteDisplay.h> 53#include <media/IRemoteDisplayClient.h> 54#include <media/MediaPlayerInterface.h> 55#include <media/mediarecorder.h> 56#include <media/MediaMetadataRetrieverInterface.h> 57#include <media/Metadata.h> 58#include <media/AudioTrack.h> 59#include <media/MemoryLeakTrackUtil.h> 60#include <media/stagefright/MediaCodecList.h> 61#include <media/stagefright/MediaErrors.h> 62#include <media/stagefright/Utils.h> 63#include <media/stagefright/foundation/ADebug.h> 64#include <media/stagefright/foundation/ALooperRoster.h> 65#include <mediautils/BatteryNotifier.h> 66 67//#include <memunreachable/memunreachable.h> 68#include <system/audio.h> 69 70#include <private/android_filesystem_config.h> 71 72#include "MediaAnalyticsService.h" 73 74#include "MetricsSummarizer.h" 75#include "MetricsSummarizerCodec.h" 76#include "MetricsSummarizerExtractor.h" 77#include "MetricsSummarizerPlayer.h" 78#include "MetricsSummarizerRecorder.h" 79 80 81namespace android { 82 83 84 85// summarized records 86// up to 48 sets, each covering an hour -- at least 2 days of coverage 87// (will be longer if there are hours without any media action) 88static const nsecs_t kNewSetIntervalNs = 3600*(1000*1000*1000ll); 89static const int kMaxRecordSets = 48; 90// individual records kept in memory 91static const int kMaxRecords = 100; 92 93 94static const char *kServiceName = "media.metrics"; 95 96 97//using android::status_t; 98//using android::OK; 99//using android::BAD_VALUE; 100//using android::NOT_ENOUGH_DATA; 101//using android::Parcel; 102 103 104void MediaAnalyticsService::instantiate() { 105 defaultServiceManager()->addService( 106 String16(kServiceName), new MediaAnalyticsService()); 107} 108 109// handle sets of summarizers 110MediaAnalyticsService::SummarizerSet::SummarizerSet() { 111 mSummarizers = new List<MetricsSummarizer *>(); 112} 113MediaAnalyticsService::SummarizerSet::~SummarizerSet() { 114 // empty the list 115 List<MetricsSummarizer *> *l = mSummarizers; 116 while (l->size() > 0) { 117 MetricsSummarizer *summarizer = *(l->begin()); 118 l->erase(l->begin()); 119 delete summarizer; 120 } 121} 122 123void MediaAnalyticsService::newSummarizerSet() { 124 ALOGD("MediaAnalyticsService::newSummarizerSet"); 125 MediaAnalyticsService::SummarizerSet *set = new MediaAnalyticsService::SummarizerSet(); 126 nsecs_t now = systemTime(SYSTEM_TIME_REALTIME); 127 set->setStarted(now); 128 129 set->appendSummarizer(new MetricsSummarizerExtractor("extractor")); 130 set->appendSummarizer(new MetricsSummarizerCodec("codec")); 131 set->appendSummarizer(new MetricsSummarizerPlayer("nuplayer")); 132 set->appendSummarizer(new MetricsSummarizerRecorder("recorder")); 133 134 // ALWAYS at the end, since it catches everything 135 set->appendSummarizer(new MetricsSummarizer(NULL)); 136 137 // inject this set at the BACK of the list. 138 mSummarizerSets->push_back(set); 139 mCurrentSet = set; 140 141 // limit the # that we have 142 if (mMaxRecordSets > 0) { 143 List<SummarizerSet *> *l = mSummarizerSets; 144 while (l->size() > (size_t) mMaxRecordSets) { 145 ALOGD("Deleting oldest record set...."); 146 MediaAnalyticsService::SummarizerSet *oset = *(l->begin()); 147 l->erase(l->begin()); 148 delete oset; 149 mSetsDiscarded++; 150 } 151 } 152} 153 154MediaAnalyticsService::MediaAnalyticsService() 155 : mMaxRecords(kMaxRecords), 156 mMaxRecordSets(kMaxRecordSets), 157 mNewSetInterval(kNewSetIntervalNs) { 158 159 ALOGD("MediaAnalyticsService created"); 160 // clear our queues 161 mOpen = new List<MediaAnalyticsItem *>(); 162 mFinalized = new List<MediaAnalyticsItem *>(); 163 164 mSummarizerSets = new List<MediaAnalyticsService::SummarizerSet *>(); 165 newSummarizerSet(); 166 167 mItemsSubmitted = 0; 168 mItemsFinalized = 0; 169 mItemsDiscarded = 0; 170 171 mLastSessionID = 0; 172 // recover any persistency we set up 173 // etc 174} 175 176MediaAnalyticsService::~MediaAnalyticsService() { 177 ALOGD("MediaAnalyticsService destroyed"); 178 179 // clean out mOpen and mFinalized 180 delete mOpen; 181 mOpen = NULL; 182 delete mFinalized; 183 mFinalized = NULL; 184 185 // XXX: clean out the summaries 186} 187 188 189MediaAnalyticsItem::SessionID_t MediaAnalyticsService::generateUniqueSessionID() { 190 // generate a new sessionid 191 192 Mutex::Autolock _l(mLock_ids); 193 return (++mLastSessionID); 194} 195 196// caller surrenders ownership of 'item' 197MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(MediaAnalyticsItem *item, bool forcenew) { 198 199 MediaAnalyticsItem::SessionID_t id = MediaAnalyticsItem::SessionIDInvalid; 200 201 // we control these, generally not trusting user input 202 nsecs_t now = systemTime(SYSTEM_TIME_REALTIME); 203 item->setTimestamp(now); 204 int pid = IPCThreadState::self()->getCallingPid(); 205 int uid = IPCThreadState::self()->getCallingUid(); 206 207 int uid_given = item->getUid(); 208 int pid_given = item->getPid(); 209 210 // although we do make exceptions for particular client uids 211 // that we know we trust. 212 // 213 bool isTrusted = false; 214 215 switch (uid) { 216 case AID_MEDIA: 217 case AID_MEDIA_CODEC: 218 case AID_MEDIA_EX: 219 case AID_MEDIA_DRM: 220 // trusted source, only override default values 221 isTrusted = true; 222 if (uid_given == (-1)) { 223 item->setUid(uid); 224 } 225 if (pid_given == (-1)) { 226 item->setPid(pid); 227 } 228 break; 229 default: 230 isTrusted = false; 231 item->setPid(pid); 232 item->setUid(uid); 233 break; 234 } 235 236 237 mItemsSubmitted++; 238 239 // validate the record; we discard if we don't like it 240 if (contentValid(item, isTrusted) == false) { 241 delete item; 242 return MediaAnalyticsItem::SessionIDInvalid; 243 } 244 245 246 // if we have a sesisonid in the new record, look to make 247 // sure it doesn't appear in the finalized list. 248 // XXX: this is for security / DOS prevention. 249 // may also require that we persist the unique sessionIDs 250 // across boots [instead of within a single boot] 251 252 253 // match this new record up against records in the open 254 // list... 255 // if there's a match, merge them together 256 // deal with moving the old / merged record into the finalized que 257 258 bool finalizing = item->getFinalized(); 259 260 // if finalizing, we'll remove it 261 MediaAnalyticsItem *oitem = findItem(mOpen, item, finalizing | forcenew); 262 if (oitem != NULL) { 263 if (forcenew) { 264 // old one gets finalized, then we insert the new one 265 // so we'll have 2 records at the end of this. 266 // but don't finalize an empty record 267 if (oitem->count() == 0) { 268 // we're responsible for disposing of the dead record 269 delete oitem; 270 oitem = NULL; 271 } else { 272 oitem->setFinalized(true); 273 summarize(oitem); 274 saveItem(mFinalized, oitem, 0); 275 } 276 // new record could itself be marked finalized... 277 if (finalizing) { 278 summarize(item); 279 saveItem(mFinalized, item, 0); 280 mItemsFinalized++; 281 } else { 282 saveItem(mOpen, item, 1); 283 } 284 id = item->getSessionID(); 285 } else { 286 // combine the records, send it to finalized if appropriate 287 oitem->merge(item); 288 if (finalizing) { 289 summarize(oitem); 290 saveItem(mFinalized, oitem, 0); 291 mItemsFinalized++; 292 } 293 id = oitem->getSessionID(); 294 295 // we're responsible for disposing of the dead record 296 delete item; 297 item = NULL; 298 } 299 } else { 300 // nothing to merge, save the new record 301 id = item->getSessionID(); 302 if (finalizing) { 303 if (item->count() == 0) { 304 // drop empty records 305 delete item; 306 item = NULL; 307 } else { 308 summarize(item); 309 saveItem(mFinalized, item, 0); 310 mItemsFinalized++; 311 } 312 } else { 313 saveItem(mOpen, item, 1); 314 } 315 } 316 return id; 317} 318 319status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args) 320{ 321 const size_t SIZE = 512; 322 char buffer[SIZE]; 323 String8 result; 324 325 if (checkCallingPermission(String16("android.permission.DUMP")) == false) { 326 snprintf(buffer, SIZE, "Permission Denial: " 327 "can't dump MediaAnalyticsService from pid=%d, uid=%d\n", 328 IPCThreadState::self()->getCallingPid(), 329 IPCThreadState::self()->getCallingUid()); 330 result.append(buffer); 331 write(fd, result.string(), result.size()); 332 return NO_ERROR; 333 } 334 335 // crack any parameters 336 bool clear = false; 337 bool summary = false; 338 nsecs_t ts_since = 0; 339 String16 summaryOption("-summary"); 340 String16 clearOption("-clear"); 341 String16 sinceOption("-since"); 342 String16 helpOption("-help"); 343 String16 onlyOption("-only"); 344 const char *only = NULL; 345 int n = args.size(); 346 for (int i = 0; i < n; i++) { 347 String8 myarg(args[i]); 348 if (args[i] == clearOption) { 349 clear = true; 350 } else if (args[i] == summaryOption) { 351 summary = true; 352 } else if (args[i] == sinceOption) { 353 i++; 354 if (i < n) { 355 String8 value(args[i]); 356 char *endp; 357 const char *p = value.string(); 358 ts_since = strtoll(p, &endp, 10); 359 if (endp == p || *endp != '\0') { 360 ts_since = 0; 361 } 362 } else { 363 ts_since = 0; 364 } 365 // command line is milliseconds; internal units are nano-seconds 366 ts_since *= 1000*1000; 367 } else if (args[i] == onlyOption) { 368 i++; 369 if (i < n) { 370 String8 value(args[i]); 371 const char *p = value.string(); 372 char *q = strdup(p); 373 if (q != NULL) { 374 if (only != NULL) { 375 free((void*)only); 376 } 377 only = q; 378 } 379 } 380 } else if (args[i] == helpOption) { 381 result.append("Recognized parameters:\n"); 382 result.append("-help this help message\n"); 383 result.append("-summary show summary info\n"); 384 result.append("-clear clears out saved records\n"); 385 result.append("-only X process records for component X\n"); 386 result.append("-since X include records since X\n"); 387 result.append(" (X is milliseconds since the UNIX epoch)\n"); 388 write(fd, result.string(), result.size()); 389 return NO_ERROR; 390 } 391 } 392 393 Mutex::Autolock _l(mLock); 394 395 // we ALWAYS dump this piece 396 snprintf(buffer, SIZE, "Dump of the %s process:\n", kServiceName); 397 result.append(buffer); 398 399 dumpHeaders(result, ts_since); 400 401 // only want 1, to avoid confusing folks that parse the output 402 if (summary) { 403 dumpSummaries(result, ts_since, only); 404 } else { 405 dumpRecent(result, ts_since, only); 406 } 407 408 409 if (clear) { 410 // remove everything from the finalized queue 411 while (mFinalized->size() > 0) { 412 MediaAnalyticsItem * oitem = *(mFinalized->begin()); 413 mFinalized->erase(mFinalized->begin()); 414 delete oitem; 415 mItemsDiscarded++; 416 } 417 418 // shall we clear the summary data too? 419 420 } 421 422 write(fd, result.string(), result.size()); 423 return NO_ERROR; 424} 425 426// dump headers 427void MediaAnalyticsService::dumpHeaders(String8 &result, nsecs_t ts_since) { 428 const size_t SIZE = 512; 429 char buffer[SIZE]; 430 431 int enabled = MediaAnalyticsItem::isEnabled(); 432 if (enabled) { 433 snprintf(buffer, SIZE, "Metrics gathering: enabled\n"); 434 } else { 435 snprintf(buffer, SIZE, "Metrics gathering: DISABLED via property\n"); 436 } 437 result.append(buffer); 438 439 snprintf(buffer, SIZE, 440 "Since Boot: Submissions: %" PRId64 441 " Finalizations: %" PRId64 442 " Discarded: %" PRId64 "\n", 443 mItemsSubmitted, mItemsFinalized, mItemsDiscarded); 444 result.append(buffer); 445 snprintf(buffer, SIZE, 446 "Summary Sets Discarded: %" PRId64 "\n", mSetsDiscarded); 447 result.append(buffer); 448 if (ts_since != 0) { 449 snprintf(buffer, SIZE, 450 "Dumping Queue entries more recent than: %" PRId64 "\n", 451 (int64_t) ts_since); 452 result.append(buffer); 453 } 454} 455 456// dump summary info 457void MediaAnalyticsService::dumpSummaries(String8 &result, nsecs_t ts_since, const char *only) { 458 const size_t SIZE = 512; 459 char buffer[SIZE]; 460 int slot = 0; 461 462 snprintf(buffer, SIZE, "\nSummarized Metrics:\n"); 463 result.append(buffer); 464 465 // have each of the distillers dump records 466 if (mSummarizerSets != NULL) { 467 List<SummarizerSet *>::iterator itSet = mSummarizerSets->begin(); 468 for (; itSet != mSummarizerSets->end(); itSet++) { 469 nsecs_t when = (*itSet)->getStarted(); 470 if (when < ts_since) { 471 continue; 472 } 473 List<MetricsSummarizer *> *list = (*itSet)->getSummarizers(); 474 List<MetricsSummarizer *>::iterator it = list->begin(); 475 for (; it != list->end(); it++) { 476 if (only != NULL && strcmp(only, (*it)->getKey()) != 0) { 477 ALOGV("Told to omit '%s'", (*it)->getKey()); 478 } 479 AString distilled = (*it)->dumpSummary(slot, only); 480 result.append(distilled.c_str()); 481 } 482 } 483 } 484} 485 486// the recent, detailed queues 487void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only) { 488 const size_t SIZE = 512; 489 char buffer[SIZE]; 490 491 // show the recently recorded records 492 snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n"); 493 result.append(buffer); 494 result.append(this->dumpQueue(mFinalized, ts_since, only)); 495 496 snprintf(buffer, sizeof(buffer), "\nIn-Progress Metrics (newest first):\n"); 497 result.append(buffer); 498 result.append(this->dumpQueue(mOpen, ts_since, only)); 499 500 // show who is connected and injecting records? 501 // talk about # records fed to the 'readers' 502 // talk about # records we discarded, perhaps "discarded w/o reading" too 503} 504// caller has locked mLock... 505String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList) { 506 return dumpQueue(theList, (nsecs_t) 0, NULL); 507} 508 509String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since, const char * only) { 510 String8 result; 511 int slot = 0; 512 513 if (theList->empty()) { 514 result.append("empty\n"); 515 } else { 516 List<MediaAnalyticsItem *>::iterator it = theList->begin(); 517 for (; it != theList->end(); it++) { 518 nsecs_t when = (*it)->getTimestamp(); 519 if (when < ts_since) { 520 continue; 521 } 522 if (only != NULL && 523 strcmp(only, (*it)->getKey().c_str()) != 0) { 524 ALOGV("Omit '%s', it's not '%s'", (*it)->getKey().c_str(), only); 525 continue; 526 } 527 AString entry = (*it)->toString(); 528 result.appendFormat("%5d: %s\n", slot, entry.c_str()); 529 slot++; 530 } 531 } 532 533 return result; 534} 535 536// 537// Our Cheap in-core, non-persistent records management. 538// XXX: rewrite this to manage persistence, etc. 539 540// insert appropriately into queue 541void MediaAnalyticsService::saveItem(List<MediaAnalyticsItem *> *l, MediaAnalyticsItem * item, int front) { 542 543 Mutex::Autolock _l(mLock); 544 545 // adding at back of queue (fifo order) 546 if (front) { 547 l->push_front(item); 548 } else { 549 l->push_back(item); 550 } 551 552 // keep removing old records the front until we're in-bounds 553 if (mMaxRecords > 0) { 554 while (l->size() > (size_t) mMaxRecords) { 555 MediaAnalyticsItem * oitem = *(l->begin()); 556 l->erase(l->begin()); 557 delete oitem; 558 mItemsDiscarded++; 559 } 560 } 561} 562 563// are they alike enough that nitem can be folded into oitem? 564static bool compatibleItems(MediaAnalyticsItem * oitem, MediaAnalyticsItem * nitem) { 565 566 if (0) { 567 ALOGD("Compare: o %s n %s", 568 oitem->toString().c_str(), nitem->toString().c_str()); 569 } 570 571 // general safety 572 if (nitem->getUid() != oitem->getUid()) { 573 return false; 574 } 575 if (nitem->getPid() != oitem->getPid()) { 576 return false; 577 } 578 579 // key -- needs to match 580 if (nitem->getKey() == oitem->getKey()) { 581 // still in the game. 582 } else { 583 return false; 584 } 585 586 // session id -- empty field in new is allowed 587 MediaAnalyticsItem::SessionID_t osession = oitem->getSessionID(); 588 MediaAnalyticsItem::SessionID_t nsession = nitem->getSessionID(); 589 if (nsession != osession) { 590 // incoming '0' matches value in osession 591 if (nsession != 0) { 592 return false; 593 } 594 } 595 596 return true; 597} 598 599// find the incomplete record that this will overlay 600MediaAnalyticsItem *MediaAnalyticsService::findItem(List<MediaAnalyticsItem*> *theList, MediaAnalyticsItem *nitem, bool removeit) { 601 if (nitem == NULL) { 602 return NULL; 603 } 604 605 MediaAnalyticsItem *item = NULL; 606 607 Mutex::Autolock _l(mLock); 608 609 for (List<MediaAnalyticsItem *>::iterator it = theList->begin(); 610 it != theList->end(); it++) { 611 MediaAnalyticsItem *tmp = (*it); 612 613 if (!compatibleItems(tmp, nitem)) { 614 continue; 615 } 616 617 // we match! this is the one I want. 618 if (removeit) { 619 theList->erase(it); 620 } 621 item = tmp; 622 break; 623 } 624 return item; 625} 626 627 628// delete the indicated record 629void MediaAnalyticsService::deleteItem(List<MediaAnalyticsItem *> *l, MediaAnalyticsItem *item) { 630 631 Mutex::Autolock _l(mLock); 632 633 for (List<MediaAnalyticsItem *>::iterator it = l->begin(); 634 it != l->end(); it++) { 635 if ((*it)->getSessionID() != item->getSessionID()) 636 continue; 637 delete *it; 638 l->erase(it); 639 break; 640 } 641} 642 643static AString allowedKeys[] = 644{ 645 "codec", 646 "extractor" 647}; 648 649static const int nAllowedKeys = sizeof(allowedKeys) / sizeof(allowedKeys[0]); 650 651// are the contents good 652bool MediaAnalyticsService::contentValid(MediaAnalyticsItem *item, bool isTrusted) { 653 654 // untrusted uids can only send us a limited set of keys 655 if (isTrusted == false) { 656 // restrict to a specific set of keys 657 AString key = item->getKey(); 658 659 size_t i; 660 for(i = 0; i < nAllowedKeys; i++) { 661 if (key == allowedKeys[i]) { 662 break; 663 } 664 } 665 if (i == nAllowedKeys) { 666 ALOGD("Ignoring (key): %s", item->toString().c_str()); 667 return false; 668 } 669 } 670 671 // internal consistency 672 673 return true; 674} 675 676// are we rate limited, normally false 677bool MediaAnalyticsService::rateLimited(MediaAnalyticsItem *) { 678 679 return false; 680} 681 682// insert into the appropriate summarizer. 683// we make our own copy to save/summarize 684void MediaAnalyticsService::summarize(MediaAnalyticsItem *item) { 685 686 ALOGV("MediaAnalyticsService::summarize()"); 687 688 if (item == NULL) { 689 return; 690 } 691 692 nsecs_t now = systemTime(SYSTEM_TIME_REALTIME); 693 if (mCurrentSet == NULL 694 || (mCurrentSet->getStarted() + mNewSetInterval < now)) { 695 newSummarizerSet(); 696 } 697 698 if (mCurrentSet == NULL) { 699 return; 700 } 701 702 List<MetricsSummarizer *> *summarizers = mCurrentSet->getSummarizers(); 703 List<MetricsSummarizer *>::iterator it = summarizers->begin(); 704 for (; it != summarizers->end(); it++) { 705 if ((*it)->isMine(*item)) { 706 break; 707 } 708 } 709 if (it == summarizers->end()) { 710 ALOGD("no handler for type %s", item->getKey().c_str()); 711 return; // no handler 712 } 713 714 // invoke the summarizer. summarizer will make whatever copies 715 // it wants; the caller retains ownership of item. 716 717 (*it)->handleRecord(item); 718 719} 720 721} // namespace android 722