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 <pthread.h> 30 #include <unistd.h> 31 32 #include <string.h> 33 #include <pwd.h> 34 35 #include <cutils/atomic.h> 36 #include <cutils/properties.h> // for property_get 37 38 #include <utils/misc.h> 39 40 #include <android/content/pm/IPackageManagerNative.h> 41 42 #include <binder/IPCThreadState.h> 43 #include <binder/IServiceManager.h> 44 #include <binder/MemoryHeapBase.h> 45 #include <binder/MemoryBase.h> 46 #include <gui/Surface.h> 47 #include <utils/Errors.h> // for status_t 48 #include <utils/List.h> 49 #include <utils/String8.h> 50 #include <utils/SystemClock.h> 51 #include <utils/Timers.h> 52 #include <utils/Vector.h> 53 54 #include <media/IMediaHTTPService.h> 55 #include <media/IRemoteDisplay.h> 56 #include <media/IRemoteDisplayClient.h> 57 #include <media/MediaPlayerInterface.h> 58 #include <media/mediarecorder.h> 59 #include <media/MediaMetadataRetrieverInterface.h> 60 #include <media/Metadata.h> 61 #include <media/AudioTrack.h> 62 #include <media/MemoryLeakTrackUtil.h> 63 #include <media/stagefright/MediaCodecList.h> 64 #include <media/stagefright/MediaErrors.h> 65 #include <media/stagefright/Utils.h> 66 #include <media/stagefright/foundation/ADebug.h> 67 #include <media/stagefright/foundation/ALooperRoster.h> 68 #include <mediautils/BatteryNotifier.h> 69 70 //#include <memunreachable/memunreachable.h> 71 #include <system/audio.h> 72 73 #include <private/android_filesystem_config.h> 74 75 #include "MediaAnalyticsService.h" 76 77 namespace android { 78 79 // individual records kept in memory: age or count 80 // age: <= 28 hours (1 1/6 days) 81 // count: hard limit of # records 82 // (0 for either of these disables that threshold) 83 // 84 static constexpr nsecs_t kMaxRecordAgeNs = 28 * 3600 * (1000*1000*1000ll); 85 // 2019/6: average daily per device is currently 375-ish; 86 // setting this to 2000 is large enough to catch most devices 87 // we'll lose some data on very very media-active devices, but only for 88 // the gms collection; statsd will have already covered those for us. 89 // This also retains enough information to help with bugreports 90 static constexpr int kMaxRecords = 2000; 91 92 // max we expire in a single call, to constrain how long we hold the 93 // mutex, which also constrains how long a client might wait. 94 static constexpr int kMaxExpiredAtOnce = 50; 95 96 // TODO: need to look at tuning kMaxRecords and friends for low-memory devices 97 98 static const char *kServiceName = "media.metrics"; 99 100 void MediaAnalyticsService::instantiate() { 101 defaultServiceManager()->addService( 102 String16(kServiceName), new MediaAnalyticsService()); 103 } 104 105 MediaAnalyticsService::MediaAnalyticsService() 106 : mMaxRecords(kMaxRecords), 107 mMaxRecordAgeNs(kMaxRecordAgeNs), 108 mMaxRecordsExpiredAtOnce(kMaxExpiredAtOnce), 109 mDumpProto(MediaAnalyticsItem::PROTO_V1), 110 mDumpProtoDefault(MediaAnalyticsItem::PROTO_V1) { 111 112 ALOGD("MediaAnalyticsService created"); 113 114 mItemsSubmitted = 0; 115 mItemsFinalized = 0; 116 mItemsDiscarded = 0; 117 mItemsDiscardedExpire = 0; 118 mItemsDiscardedCount = 0; 119 120 mLastSessionID = 0; 121 // recover any persistency we set up 122 // etc 123 } 124 125 MediaAnalyticsService::~MediaAnalyticsService() { 126 ALOGD("MediaAnalyticsService destroyed"); 127 128 while (mItems.size() > 0) { 129 MediaAnalyticsItem * oitem = *(mItems.begin()); 130 mItems.erase(mItems.begin()); 131 delete oitem; 132 mItemsDiscarded++; 133 mItemsDiscardedCount++; 134 } 135 } 136 137 138 MediaAnalyticsItem::SessionID_t MediaAnalyticsService::generateUniqueSessionID() { 139 // generate a new sessionid 140 141 Mutex::Autolock _l(mLock_ids); 142 return (++mLastSessionID); 143 } 144 145 // caller surrenders ownership of 'item' 146 MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(MediaAnalyticsItem *item, bool forcenew) 147 { 148 UNUSED(forcenew); 149 150 // fill in a sessionID if we do not yet have one 151 if (item->getSessionID() <= MediaAnalyticsItem::SessionIDNone) { 152 item->setSessionID(generateUniqueSessionID()); 153 } 154 155 // we control these, generally not trusting user input 156 nsecs_t now = systemTime(SYSTEM_TIME_REALTIME); 157 // round nsecs to seconds 158 now = ((now + 500000000) / 1000000000) * 1000000000; 159 item->setTimestamp(now); 160 int pid = IPCThreadState::self()->getCallingPid(); 161 int uid = IPCThreadState::self()->getCallingUid(); 162 163 int uid_given = item->getUid(); 164 int pid_given = item->getPid(); 165 166 // although we do make exceptions for some trusted client uids 167 bool isTrusted = false; 168 169 ALOGV("caller has uid=%d, embedded uid=%d", uid, uid_given); 170 171 switch (uid) { 172 case AID_MEDIA: 173 case AID_MEDIA_CODEC: 174 case AID_MEDIA_EX: 175 case AID_MEDIA_DRM: 176 // trusted source, only override default values 177 isTrusted = true; 178 if (uid_given == (-1)) { 179 item->setUid(uid); 180 } 181 if (pid_given == (-1)) { 182 item->setPid(pid); 183 } 184 break; 185 default: 186 isTrusted = false; 187 item->setPid(pid); 188 item->setUid(uid); 189 break; 190 } 191 192 // Overwrite package name and version if the caller was untrusted. 193 if (!isTrusted) { 194 setPkgInfo(item, item->getUid(), true, true); 195 } else if (item->getPkgName().empty()) { 196 // empty, so fill out both parts 197 setPkgInfo(item, item->getUid(), true, true); 198 } else { 199 // trusted, provided a package, do nothing 200 } 201 202 ALOGV("given uid %d; sanitized uid: %d sanitized pkg: %s " 203 "sanitized pkg version: %" PRId64, 204 uid_given, item->getUid(), 205 item->getPkgName().c_str(), 206 item->getPkgVersionCode()); 207 208 mItemsSubmitted++; 209 210 // validate the record; we discard if we don't like it 211 if (contentValid(item, isTrusted) == false) { 212 delete item; 213 return MediaAnalyticsItem::SessionIDInvalid; 214 } 215 216 // XXX: if we have a sessionid in the new record, look to make 217 // sure it doesn't appear in the finalized list. 218 219 if (item->count() == 0) { 220 ALOGV("dropping empty record..."); 221 delete item; 222 item = NULL; 223 return MediaAnalyticsItem::SessionIDInvalid; 224 } 225 226 // save the new record 227 // 228 // send a copy to statsd 229 dump2Statsd(item); 230 231 // and keep our copy for dumpsys 232 MediaAnalyticsItem::SessionID_t id = item->getSessionID(); 233 saveItem(item); 234 mItemsFinalized++; 235 236 return id; 237 } 238 239 240 status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args) 241 { 242 const size_t SIZE = 512; 243 char buffer[SIZE]; 244 String8 result; 245 246 if (checkCallingPermission(String16("android.permission.DUMP")) == false) { 247 snprintf(buffer, SIZE, "Permission Denial: " 248 "can't dump MediaAnalyticsService from pid=%d, uid=%d\n", 249 IPCThreadState::self()->getCallingPid(), 250 IPCThreadState::self()->getCallingUid()); 251 result.append(buffer); 252 write(fd, result.string(), result.size()); 253 return NO_ERROR; 254 } 255 256 // crack any parameters 257 String16 protoOption("-proto"); 258 int chosenProto = mDumpProtoDefault; 259 String16 clearOption("-clear"); 260 bool clear = false; 261 String16 sinceOption("-since"); 262 nsecs_t ts_since = 0; 263 String16 helpOption("-help"); 264 String16 onlyOption("-only"); 265 std::string only; 266 int n = args.size(); 267 268 for (int i = 0; i < n; i++) { 269 String8 myarg(args[i]); 270 if (args[i] == clearOption) { 271 clear = true; 272 } else if (args[i] == protoOption) { 273 i++; 274 if (i < n) { 275 String8 value(args[i]); 276 int proto = MediaAnalyticsItem::PROTO_V0; 277 char *endp; 278 const char *p = value.string(); 279 proto = strtol(p, &endp, 10); 280 if (endp != p || *endp == '\0') { 281 if (proto < MediaAnalyticsItem::PROTO_FIRST) { 282 proto = MediaAnalyticsItem::PROTO_FIRST; 283 } else if (proto > MediaAnalyticsItem::PROTO_LAST) { 284 proto = MediaAnalyticsItem::PROTO_LAST; 285 } 286 chosenProto = proto; 287 } else { 288 result.append("unable to parse value for -proto\n\n"); 289 } 290 } else { 291 result.append("missing value for -proto\n\n"); 292 } 293 } else if (args[i] == sinceOption) { 294 i++; 295 if (i < n) { 296 String8 value(args[i]); 297 char *endp; 298 const char *p = value.string(); 299 ts_since = strtoll(p, &endp, 10); 300 if (endp == p || *endp != '\0') { 301 ts_since = 0; 302 } 303 } else { 304 ts_since = 0; 305 } 306 // command line is milliseconds; internal units are nano-seconds 307 ts_since *= 1000*1000; 308 } else if (args[i] == onlyOption) { 309 i++; 310 if (i < n) { 311 String8 value(args[i]); 312 only = value.string(); 313 } 314 } else if (args[i] == helpOption) { 315 result.append("Recognized parameters:\n"); 316 result.append("-help this help message\n"); 317 result.append("-proto # dump using protocol #"); 318 result.append("-clear clears out saved records\n"); 319 result.append("-only X process records for component X\n"); 320 result.append("-since X include records since X\n"); 321 result.append(" (X is milliseconds since the UNIX epoch)\n"); 322 write(fd, result.string(), result.size()); 323 return NO_ERROR; 324 } 325 } 326 327 Mutex::Autolock _l(mLock); 328 // mutex between insertion and dumping the contents 329 330 mDumpProto = chosenProto; 331 332 // we ALWAYS dump this piece 333 snprintf(buffer, SIZE, "Dump of the %s process:\n", kServiceName); 334 result.append(buffer); 335 336 dumpHeaders(result, ts_since); 337 338 dumpRecent(result, ts_since, only.c_str()); 339 340 341 if (clear) { 342 // remove everything from the finalized queue 343 while (mItems.size() > 0) { 344 MediaAnalyticsItem * oitem = *(mItems.begin()); 345 mItems.erase(mItems.begin()); 346 delete oitem; 347 mItemsDiscarded++; 348 } 349 350 // shall we clear the summary data too? 351 352 } 353 354 write(fd, result.string(), result.size()); 355 return NO_ERROR; 356 } 357 358 // dump headers 359 void MediaAnalyticsService::dumpHeaders(String8 &result, nsecs_t ts_since) 360 { 361 const size_t SIZE = 512; 362 char buffer[SIZE]; 363 364 snprintf(buffer, SIZE, "Protocol Version: %d\n", mDumpProto); 365 result.append(buffer); 366 367 int enabled = MediaAnalyticsItem::isEnabled(); 368 if (enabled) { 369 snprintf(buffer, SIZE, "Metrics gathering: enabled\n"); 370 } else { 371 snprintf(buffer, SIZE, "Metrics gathering: DISABLED via property\n"); 372 } 373 result.append(buffer); 374 375 snprintf(buffer, SIZE, 376 "Since Boot: Submissions: %8" PRId64 377 " Accepted: %8" PRId64 "\n", 378 mItemsSubmitted, mItemsFinalized); 379 result.append(buffer); 380 snprintf(buffer, SIZE, 381 "Records Discarded: %8" PRId64 382 " (by Count: %" PRId64 " by Expiration: %" PRId64 ")\n", 383 mItemsDiscarded, mItemsDiscardedCount, mItemsDiscardedExpire); 384 result.append(buffer); 385 if (ts_since != 0) { 386 snprintf(buffer, SIZE, 387 "Emitting Queue entries more recent than: %" PRId64 "\n", 388 (int64_t) ts_since); 389 result.append(buffer); 390 } 391 } 392 393 // the recent, detailed queues 394 void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only) 395 { 396 const size_t SIZE = 512; 397 char buffer[SIZE]; 398 399 if (only != NULL && *only == '\0') { 400 only = NULL; 401 } 402 403 // show the recently recorded records 404 snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n"); 405 result.append(buffer); 406 result.append(this->dumpQueue(ts_since, only)); 407 408 // show who is connected and injecting records? 409 // talk about # records fed to the 'readers' 410 // talk about # records we discarded, perhaps "discarded w/o reading" too 411 } 412 413 // caller has locked mLock... 414 String8 MediaAnalyticsService::dumpQueue() { 415 return dumpQueue((nsecs_t) 0, NULL); 416 } 417 418 String8 MediaAnalyticsService::dumpQueue(nsecs_t ts_since, const char * only) { 419 String8 result; 420 int slot = 0; 421 422 if (mItems.empty()) { 423 result.append("empty\n"); 424 } else { 425 List<MediaAnalyticsItem *>::iterator it = mItems.begin(); 426 for (; it != mItems.end(); it++) { 427 nsecs_t when = (*it)->getTimestamp(); 428 if (when < ts_since) { 429 continue; 430 } 431 if (only != NULL && 432 strcmp(only, (*it)->getKey().c_str()) != 0) { 433 ALOGV("Omit '%s', it's not '%s'", (*it)->getKey().c_str(), only); 434 continue; 435 } 436 std::string entry = (*it)->toString(mDumpProto); 437 result.appendFormat("%5d: %s\n", slot, entry.c_str()); 438 slot++; 439 } 440 } 441 442 return result; 443 } 444 445 // 446 // Our Cheap in-core, non-persistent records management. 447 448 449 // we hold mLock when we get here 450 // if item != NULL, it's the item we just inserted 451 // true == more items eligible to be recovered 452 bool MediaAnalyticsService::expirations_l(MediaAnalyticsItem *item) 453 { 454 bool more = false; 455 int handled = 0; 456 457 // keep removing old records the front until we're in-bounds (count) 458 // since we invoke this with each insertion, it should be 0/1 iterations. 459 if (mMaxRecords > 0) { 460 while (mItems.size() > (size_t) mMaxRecords) { 461 MediaAnalyticsItem * oitem = *(mItems.begin()); 462 if (oitem == item) { 463 break; 464 } 465 if (handled >= mMaxRecordsExpiredAtOnce) { 466 // unlikely in this loop 467 more = true; 468 break; 469 } 470 handled++; 471 mItems.erase(mItems.begin()); 472 delete oitem; 473 mItemsDiscarded++; 474 mItemsDiscardedCount++; 475 } 476 } 477 478 // keep removing old records the front until we're in-bounds (age) 479 // limited to mMaxRecordsExpiredAtOnce items per invocation. 480 if (mMaxRecordAgeNs > 0) { 481 nsecs_t now = systemTime(SYSTEM_TIME_REALTIME); 482 while (mItems.size() > 0) { 483 MediaAnalyticsItem * oitem = *(mItems.begin()); 484 nsecs_t when = oitem->getTimestamp(); 485 if (oitem == item) { 486 break; 487 } 488 // careful about timejumps too 489 if ((now > when) && (now-when) <= mMaxRecordAgeNs) { 490 // this (and the rest) are recent enough to keep 491 break; 492 } 493 if (handled >= mMaxRecordsExpiredAtOnce) { 494 // this represents "one too many"; tell caller there are 495 // more to be reclaimed. 496 more = true; 497 break; 498 } 499 handled++; 500 mItems.erase(mItems.begin()); 501 delete oitem; 502 mItemsDiscarded++; 503 mItemsDiscardedExpire++; 504 } 505 } 506 507 // we only indicate whether there's more to clean; 508 // caller chooses whether to schedule further cleanup. 509 return more; 510 } 511 512 // process expirations in bite sized chunks, allowing new insertions through 513 // runs in a pthread specifically started for this (which then exits) 514 bool MediaAnalyticsService::processExpirations() 515 { 516 bool more; 517 do { 518 sleep(1); 519 { 520 Mutex::Autolock _l(mLock); 521 more = expirations_l(NULL); 522 if (!more) { 523 break; 524 } 525 } 526 } while (more); 527 return true; // value is for std::future thread synchronization 528 } 529 530 // insert appropriately into queue 531 void MediaAnalyticsService::saveItem(MediaAnalyticsItem * item) 532 { 533 534 Mutex::Autolock _l(mLock); 535 // mutex between insertion and dumping the contents 536 537 // we want to dump 'in FIFO order', so insert at the end 538 mItems.push_back(item); 539 540 // clean old stuff from the queue 541 bool more = expirations_l(item); 542 543 // consider scheduling some asynchronous cleaning, if not running 544 if (more) { 545 if (!mExpireFuture.valid() 546 || mExpireFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { 547 548 mExpireFuture = std::async(std::launch::async, [this]() 549 {return this->processExpirations();}); 550 } 551 } 552 } 553 554 static std::string allowedKeys[] = 555 { 556 "audiopolicy", 557 "audiorecord", 558 "audiothread", 559 "audiotrack", 560 "codec", 561 "extractor", 562 "nuplayer", 563 }; 564 565 static const int nAllowedKeys = sizeof(allowedKeys) / sizeof(allowedKeys[0]); 566 567 // are the contents good 568 bool MediaAnalyticsService::contentValid(MediaAnalyticsItem *item, bool isTrusted) { 569 570 // untrusted uids can only send us a limited set of keys 571 if (isTrusted == false) { 572 // restrict to a specific set of keys 573 std::string key = item->getKey(); 574 575 size_t i; 576 for(i = 0; i < nAllowedKeys; i++) { 577 if (key == allowedKeys[i]) { 578 break; 579 } 580 } 581 if (i == nAllowedKeys) { 582 ALOGD("Ignoring (key): %s", item->toString().c_str()); 583 return false; 584 } 585 } 586 587 // internal consistency 588 589 return true; 590 } 591 592 // are we rate limited, normally false 593 bool MediaAnalyticsService::rateLimited(MediaAnalyticsItem *) { 594 595 return false; 596 } 597 598 // how long we hold package info before we re-fetch it 599 #define PKG_EXPIRATION_NS (30*60*1000000000ll) // 30 minutes, in nsecs 600 601 // give me the package name, perhaps going to find it 602 // manages its own mutex operations internally 603 void MediaAnalyticsService::setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion) 604 { 605 ALOGV("asking for packagename to go with uid=%d", uid); 606 607 if (!setName && !setVersion) { 608 // setting nothing? strange 609 return; 610 } 611 612 nsecs_t now = systemTime(SYSTEM_TIME_REALTIME); 613 struct UidToPkgMap mapping; 614 mapping.uid = (uid_t)(-1); 615 616 { 617 Mutex::Autolock _l(mLock_mappings); 618 int i = mPkgMappings.indexOfKey(uid); 619 if (i >= 0) { 620 mapping = mPkgMappings.valueAt(i); 621 ALOGV("Expiration? uid %d expiration %" PRId64 " now %" PRId64, 622 uid, mapping.expiration, now); 623 if (mapping.expiration <= now) { 624 // purge the stale entry and fall into re-fetching 625 ALOGV("entry for uid %d expired, now= %" PRId64 "", uid, now); 626 mPkgMappings.removeItemsAt(i); 627 mapping.uid = (uid_t)(-1); 628 } 629 } 630 } 631 632 // if we did not find it 633 if (mapping.uid == (uid_t)(-1)) { 634 std::string pkg; 635 std::string installer = ""; 636 int64_t versionCode = 0; 637 638 struct passwd *pw = getpwuid(uid); 639 if (pw) { 640 pkg = pw->pw_name; 641 } 642 643 // find the proper value 644 645 sp<IBinder> binder = NULL; 646 sp<IServiceManager> sm = defaultServiceManager(); 647 if (sm == NULL) { 648 ALOGE("defaultServiceManager failed"); 649 } else { 650 binder = sm->getService(String16("package_native")); 651 if (binder == NULL) { 652 ALOGE("getService package_native failed"); 653 } 654 } 655 656 if (binder != NULL) { 657 sp<content::pm::IPackageManagerNative> package_mgr = 658 interface_cast<content::pm::IPackageManagerNative>(binder); 659 binder::Status status; 660 661 std::vector<int> uids; 662 std::vector<std::string> names; 663 664 uids.push_back(uid); 665 666 status = package_mgr->getNamesForUids(uids, &names); 667 if (!status.isOk()) { 668 ALOGE("package_native::getNamesForUids failed: %s", 669 status.exceptionMessage().c_str()); 670 } else { 671 if (!names[0].empty()) { 672 pkg = names[0].c_str(); 673 } 674 } 675 676 // strip any leading "shared:" strings that came back 677 if (pkg.compare(0, 7, "shared:") == 0) { 678 pkg.erase(0, 7); 679 } 680 681 // determine how pkg was installed and the versionCode 682 // 683 if (pkg.empty()) { 684 // no name for us to manage 685 } else if (strchr(pkg.c_str(), '.') == NULL) { 686 // not of form 'com.whatever...'; assume internal and ok 687 } else if (strncmp(pkg.c_str(), "android.", 8) == 0) { 688 // android.* packages are assumed fine 689 } else { 690 String16 pkgName16(pkg.c_str()); 691 status = package_mgr->getInstallerForPackage(pkgName16, &installer); 692 if (!status.isOk()) { 693 ALOGE("package_native::getInstallerForPackage failed: %s", 694 status.exceptionMessage().c_str()); 695 } 696 697 // skip if we didn't get an installer 698 if (status.isOk()) { 699 status = package_mgr->getVersionCodeForPackage(pkgName16, &versionCode); 700 if (!status.isOk()) { 701 ALOGE("package_native::getVersionCodeForPackage failed: %s", 702 status.exceptionMessage().c_str()); 703 } 704 } 705 706 707 ALOGV("package '%s' installed by '%s' versioncode %" PRId64 " / %" PRIx64, 708 pkg.c_str(), installer.c_str(), versionCode, versionCode); 709 710 if (strncmp(installer.c_str(), "com.android.", 12) == 0) { 711 // from play store, we keep info 712 } else if (strncmp(installer.c_str(), "com.google.", 11) == 0) { 713 // some google source, we keep info 714 } else if (strcmp(installer.c_str(), "preload") == 0) { 715 // preloads, we keep the info 716 } else if (installer.c_str()[0] == '\0') { 717 // sideload (no installer); do not report 718 pkg = ""; 719 versionCode = 0; 720 } else { 721 // unknown installer; do not report 722 pkg = ""; 723 versionCode = 0; 724 } 725 } 726 } 727 728 // add it to the map, to save a subsequent lookup 729 if (!pkg.empty()) { 730 Mutex::Autolock _l(mLock_mappings); 731 ALOGV("Adding uid %d pkg '%s'", uid, pkg.c_str()); 732 ssize_t i = mPkgMappings.indexOfKey(uid); 733 if (i < 0) { 734 mapping.uid = uid; 735 mapping.pkg = pkg; 736 mapping.installer = installer.c_str(); 737 mapping.versionCode = versionCode; 738 mapping.expiration = now + PKG_EXPIRATION_NS; 739 ALOGV("expiration for uid %d set to %" PRId64 "", uid, mapping.expiration); 740 741 mPkgMappings.add(uid, mapping); 742 } 743 } 744 } 745 746 if (mapping.uid != (uid_t)(-1)) { 747 if (setName) { 748 item->setPkgName(mapping.pkg); 749 } 750 if (setVersion) { 751 item->setPkgVersionCode(mapping.versionCode); 752 } 753 } 754 } 755 756 } // namespace android 757