1 /* 2 ** 3 ** Copyright 2015, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 //#define LOG_NDEBUG 0 19 #define LOG_TAG "ResourceManagerService" 20 #include <utils/Log.h> 21 22 #include <binder/IMediaResourceMonitor.h> 23 #include <binder/IServiceManager.h> 24 #include <dirent.h> 25 #include <media/stagefright/ProcessInfo.h> 26 #include <string.h> 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <sys/time.h> 30 #include <unistd.h> 31 32 #include "ResourceManagerService.h" 33 #include "ServiceLog.h" 34 #include "mediautils/SchedulingPolicyService.h" 35 #include <cutils/sched_policy.h> 36 namespace android { 37 38 namespace { 39 40 class DeathNotifier : public IBinder::DeathRecipient { 41 public: 42 DeathNotifier(const wp<ResourceManagerService> &service, int pid, int64_t clientId) 43 : mService(service), mPid(pid), mClientId(clientId) {} 44 45 virtual void binderDied(const wp<IBinder> & /* who */) override { 46 // Don't check for pid validity since we know it's already dead. 47 sp<ResourceManagerService> service = mService.promote(); 48 if (service == nullptr) { 49 ALOGW("ResourceManagerService is dead as well."); 50 return; 51 } 52 service->removeResource(mPid, mClientId, false); 53 } 54 55 private: 56 wp<ResourceManagerService> mService; 57 int mPid; 58 int64_t mClientId; 59 }; 60 61 } // namespace 62 63 template <typename T> 64 static String8 getString(const Vector<T> &items) { 65 String8 itemsStr; 66 for (size_t i = 0; i < items.size(); ++i) { 67 itemsStr.appendFormat("%s ", items[i].toString().string()); 68 } 69 return itemsStr; 70 } 71 72 static bool hasResourceType(MediaResource::Type type, const Vector<MediaResource>& resources) { 73 for (size_t i = 0; i < resources.size(); ++i) { 74 if (resources[i].mType == type) { 75 return true; 76 } 77 } 78 return false; 79 } 80 81 static bool hasResourceType(MediaResource::Type type, const ResourceInfos& infos) { 82 for (size_t i = 0; i < infos.size(); ++i) { 83 if (hasResourceType(type, infos[i].resources)) { 84 return true; 85 } 86 } 87 return false; 88 } 89 90 static ResourceInfos& getResourceInfosForEdit( 91 int pid, 92 PidResourceInfosMap& map) { 93 ssize_t index = map.indexOfKey(pid); 94 if (index < 0) { 95 // new pid 96 ResourceInfos infosForPid; 97 map.add(pid, infosForPid); 98 } 99 100 return map.editValueFor(pid); 101 } 102 103 static ResourceInfo& getResourceInfoForEdit( 104 int64_t clientId, 105 const sp<IResourceManagerClient>& client, 106 ResourceInfos& infos) { 107 for (size_t i = 0; i < infos.size(); ++i) { 108 if (infos[i].clientId == clientId) { 109 return infos.editItemAt(i); 110 } 111 } 112 ResourceInfo info; 113 info.clientId = clientId; 114 info.client = client; 115 info.cpuBoost = false; 116 infos.push_back(info); 117 return infos.editItemAt(infos.size() - 1); 118 } 119 120 static void notifyResourceGranted(int pid, const Vector<MediaResource> &resources) { 121 static const char* const kServiceName = "media_resource_monitor"; 122 sp<IBinder> binder = defaultServiceManager()->checkService(String16(kServiceName)); 123 if (binder != NULL) { 124 sp<IMediaResourceMonitor> service = interface_cast<IMediaResourceMonitor>(binder); 125 for (size_t i = 0; i < resources.size(); ++i) { 126 if (resources[i].mSubType == MediaResource::kAudioCodec) { 127 service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_AUDIO_CODEC); 128 } else if (resources[i].mSubType == MediaResource::kVideoCodec) { 129 service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_VIDEO_CODEC); 130 } 131 } 132 } 133 } 134 135 status_t ResourceManagerService::dump(int fd, const Vector<String16>& /* args */) { 136 String8 result; 137 138 if (checkCallingPermission(String16("android.permission.DUMP")) == false) { 139 result.format("Permission Denial: " 140 "can't dump ResourceManagerService from pid=%d, uid=%d\n", 141 IPCThreadState::self()->getCallingPid(), 142 IPCThreadState::self()->getCallingUid()); 143 write(fd, result.string(), result.size()); 144 return PERMISSION_DENIED; 145 } 146 147 PidResourceInfosMap mapCopy; 148 bool supportsMultipleSecureCodecs; 149 bool supportsSecureWithNonSecureCodec; 150 String8 serviceLog; 151 { 152 Mutex::Autolock lock(mLock); 153 mapCopy = mMap; // Shadow copy, real copy will happen on write. 154 supportsMultipleSecureCodecs = mSupportsMultipleSecureCodecs; 155 supportsSecureWithNonSecureCodec = mSupportsSecureWithNonSecureCodec; 156 serviceLog = mServiceLog->toString(" " /* linePrefix */); 157 } 158 159 const size_t SIZE = 256; 160 char buffer[SIZE]; 161 snprintf(buffer, SIZE, "ResourceManagerService: %p\n", this); 162 result.append(buffer); 163 result.append(" Policies:\n"); 164 snprintf(buffer, SIZE, " SupportsMultipleSecureCodecs: %d\n", supportsMultipleSecureCodecs); 165 result.append(buffer); 166 snprintf(buffer, SIZE, " SupportsSecureWithNonSecureCodec: %d\n", 167 supportsSecureWithNonSecureCodec); 168 result.append(buffer); 169 170 result.append(" Processes:\n"); 171 for (size_t i = 0; i < mapCopy.size(); ++i) { 172 snprintf(buffer, SIZE, " Pid: %d\n", mapCopy.keyAt(i)); 173 result.append(buffer); 174 175 const ResourceInfos &infos = mapCopy.valueAt(i); 176 for (size_t j = 0; j < infos.size(); ++j) { 177 result.append(" Client:\n"); 178 snprintf(buffer, SIZE, " Id: %lld\n", (long long)infos[j].clientId); 179 result.append(buffer); 180 181 snprintf(buffer, SIZE, " Name: %s\n", infos[j].client->getName().string()); 182 result.append(buffer); 183 184 Vector<MediaResource> resources = infos[j].resources; 185 result.append(" Resources:\n"); 186 for (size_t k = 0; k < resources.size(); ++k) { 187 snprintf(buffer, SIZE, " %s\n", resources[k].toString().string()); 188 result.append(buffer); 189 } 190 } 191 } 192 result.append(" Events logs (most recent at top):\n"); 193 result.append(serviceLog); 194 195 write(fd, result.string(), result.size()); 196 return OK; 197 } 198 199 ResourceManagerService::ResourceManagerService() 200 : ResourceManagerService(new ProcessInfo()) {} 201 202 ResourceManagerService::ResourceManagerService(sp<ProcessInfoInterface> processInfo) 203 : mProcessInfo(processInfo), 204 mServiceLog(new ServiceLog()), 205 mSupportsMultipleSecureCodecs(true), 206 mSupportsSecureWithNonSecureCodec(true), 207 mCpuBoostCount(0) {} 208 209 ResourceManagerService::~ResourceManagerService() {} 210 211 void ResourceManagerService::config(const Vector<MediaResourcePolicy> &policies) { 212 String8 log = String8::format("config(%s)", getString(policies).string()); 213 mServiceLog->add(log); 214 215 Mutex::Autolock lock(mLock); 216 for (size_t i = 0; i < policies.size(); ++i) { 217 String8 type = policies[i].mType; 218 String8 value = policies[i].mValue; 219 if (type == kPolicySupportsMultipleSecureCodecs) { 220 mSupportsMultipleSecureCodecs = (value == "true"); 221 } else if (type == kPolicySupportsSecureWithNonSecureCodec) { 222 mSupportsSecureWithNonSecureCodec = (value == "true"); 223 } 224 } 225 } 226 227 void ResourceManagerService::addResource( 228 int pid, 229 int64_t clientId, 230 const sp<IResourceManagerClient> client, 231 const Vector<MediaResource> &resources) { 232 String8 log = String8::format("addResource(pid %d, clientId %lld, resources %s)", 233 pid, (long long) clientId, getString(resources).string()); 234 mServiceLog->add(log); 235 236 Mutex::Autolock lock(mLock); 237 if (!mProcessInfo->isValidPid(pid)) { 238 ALOGE("Rejected addResource call with invalid pid."); 239 return; 240 } 241 ResourceInfos& infos = getResourceInfosForEdit(pid, mMap); 242 ResourceInfo& info = getResourceInfoForEdit(clientId, client, infos); 243 // TODO: do the merge instead of append. 244 info.resources.appendVector(resources); 245 246 for (size_t i = 0; i < resources.size(); ++i) { 247 if (resources[i].mType == MediaResource::kCpuBoost && !info.cpuBoost) { 248 info.cpuBoost = true; 249 // Request it on every new instance of kCpuBoost, as the media.codec 250 // could have died, if we only do it the first time subsequent instances 251 // never gets the boost. 252 if (requestCpusetBoost(true, this) != OK) { 253 ALOGW("couldn't request cpuset boost"); 254 } 255 mCpuBoostCount++; 256 } 257 } 258 if (info.deathNotifier == nullptr) { 259 info.deathNotifier = new DeathNotifier(this, pid, clientId); 260 IInterface::asBinder(client)->linkToDeath(info.deathNotifier); 261 } 262 notifyResourceGranted(pid, resources); 263 } 264 265 void ResourceManagerService::removeResource(int pid, int64_t clientId) { 266 removeResource(pid, clientId, true); 267 } 268 269 void ResourceManagerService::removeResource(int pid, int64_t clientId, bool checkValid) { 270 String8 log = String8::format( 271 "removeResource(pid %d, clientId %lld)", 272 pid, (long long) clientId); 273 mServiceLog->add(log); 274 275 Mutex::Autolock lock(mLock); 276 if (checkValid && !mProcessInfo->isValidPid(pid)) { 277 ALOGE("Rejected removeResource call with invalid pid."); 278 return; 279 } 280 ssize_t index = mMap.indexOfKey(pid); 281 if (index < 0) { 282 ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId); 283 return; 284 } 285 bool found = false; 286 ResourceInfos &infos = mMap.editValueAt(index); 287 for (size_t j = 0; j < infos.size(); ++j) { 288 if (infos[j].clientId == clientId) { 289 if (infos[j].cpuBoost && mCpuBoostCount > 0) { 290 if (--mCpuBoostCount == 0) { 291 requestCpusetBoost(false, this); 292 } 293 } 294 IInterface::asBinder(infos[j].client)->unlinkToDeath(infos[j].deathNotifier); 295 j = infos.removeAt(j); 296 found = true; 297 break; 298 } 299 } 300 if (!found) { 301 ALOGV("didn't find client"); 302 } 303 } 304 305 void ResourceManagerService::getClientForResource_l( 306 int callingPid, const MediaResource *res, Vector<sp<IResourceManagerClient>> *clients) { 307 if (res == NULL) { 308 return; 309 } 310 sp<IResourceManagerClient> client; 311 if (getLowestPriorityBiggestClient_l(callingPid, res->mType, &client)) { 312 clients->push_back(client); 313 } 314 } 315 316 bool ResourceManagerService::reclaimResource( 317 int callingPid, const Vector<MediaResource> &resources) { 318 String8 log = String8::format("reclaimResource(callingPid %d, resources %s)", 319 callingPid, getString(resources).string()); 320 mServiceLog->add(log); 321 322 Vector<sp<IResourceManagerClient>> clients; 323 { 324 Mutex::Autolock lock(mLock); 325 if (!mProcessInfo->isValidPid(callingPid)) { 326 ALOGE("Rejected reclaimResource call with invalid callingPid."); 327 return false; 328 } 329 const MediaResource *secureCodec = NULL; 330 const MediaResource *nonSecureCodec = NULL; 331 const MediaResource *graphicMemory = NULL; 332 for (size_t i = 0; i < resources.size(); ++i) { 333 MediaResource::Type type = resources[i].mType; 334 if (resources[i].mType == MediaResource::kSecureCodec) { 335 secureCodec = &resources[i]; 336 } else if (type == MediaResource::kNonSecureCodec) { 337 nonSecureCodec = &resources[i]; 338 } else if (type == MediaResource::kGraphicMemory) { 339 graphicMemory = &resources[i]; 340 } 341 } 342 343 // first pass to handle secure/non-secure codec conflict 344 if (secureCodec != NULL) { 345 if (!mSupportsMultipleSecureCodecs) { 346 if (!getAllClients_l(callingPid, MediaResource::kSecureCodec, &clients)) { 347 return false; 348 } 349 } 350 if (!mSupportsSecureWithNonSecureCodec) { 351 if (!getAllClients_l(callingPid, MediaResource::kNonSecureCodec, &clients)) { 352 return false; 353 } 354 } 355 } 356 if (nonSecureCodec != NULL) { 357 if (!mSupportsSecureWithNonSecureCodec) { 358 if (!getAllClients_l(callingPid, MediaResource::kSecureCodec, &clients)) { 359 return false; 360 } 361 } 362 } 363 364 if (clients.size() == 0) { 365 // if no secure/non-secure codec conflict, run second pass to handle other resources. 366 getClientForResource_l(callingPid, graphicMemory, &clients); 367 } 368 369 if (clients.size() == 0) { 370 // if we are here, run the third pass to free one codec with the same type. 371 getClientForResource_l(callingPid, secureCodec, &clients); 372 getClientForResource_l(callingPid, nonSecureCodec, &clients); 373 } 374 375 if (clients.size() == 0) { 376 // if we are here, run the fourth pass to free one codec with the different type. 377 if (secureCodec != NULL) { 378 MediaResource temp(MediaResource::kNonSecureCodec, 1); 379 getClientForResource_l(callingPid, &temp, &clients); 380 } 381 if (nonSecureCodec != NULL) { 382 MediaResource temp(MediaResource::kSecureCodec, 1); 383 getClientForResource_l(callingPid, &temp, &clients); 384 } 385 } 386 } 387 388 if (clients.size() == 0) { 389 return false; 390 } 391 392 sp<IResourceManagerClient> failedClient; 393 for (size_t i = 0; i < clients.size(); ++i) { 394 log = String8::format("reclaimResource from client %p", clients[i].get()); 395 mServiceLog->add(log); 396 if (!clients[i]->reclaimResource()) { 397 failedClient = clients[i]; 398 break; 399 } 400 } 401 402 if (failedClient == NULL) { 403 return true; 404 } 405 406 { 407 Mutex::Autolock lock(mLock); 408 bool found = false; 409 for (size_t i = 0; i < mMap.size(); ++i) { 410 ResourceInfos &infos = mMap.editValueAt(i); 411 for (size_t j = 0; j < infos.size();) { 412 if (infos[j].client == failedClient) { 413 j = infos.removeAt(j); 414 found = true; 415 } else { 416 ++j; 417 } 418 } 419 if (found) { 420 break; 421 } 422 } 423 if (!found) { 424 ALOGV("didn't find failed client"); 425 } 426 } 427 428 return false; 429 } 430 431 bool ResourceManagerService::getAllClients_l( 432 int callingPid, MediaResource::Type type, Vector<sp<IResourceManagerClient>> *clients) { 433 Vector<sp<IResourceManagerClient>> temp; 434 for (size_t i = 0; i < mMap.size(); ++i) { 435 ResourceInfos &infos = mMap.editValueAt(i); 436 for (size_t j = 0; j < infos.size(); ++j) { 437 if (hasResourceType(type, infos[j].resources)) { 438 if (!isCallingPriorityHigher_l(callingPid, mMap.keyAt(i))) { 439 // some higher/equal priority process owns the resource, 440 // this request can't be fulfilled. 441 ALOGE("getAllClients_l: can't reclaim resource %s from pid %d", 442 asString(type), mMap.keyAt(i)); 443 return false; 444 } 445 temp.push_back(infos[j].client); 446 } 447 } 448 } 449 if (temp.size() == 0) { 450 ALOGV("getAllClients_l: didn't find any resource %s", asString(type)); 451 return true; 452 } 453 clients->appendVector(temp); 454 return true; 455 } 456 457 bool ResourceManagerService::getLowestPriorityBiggestClient_l( 458 int callingPid, MediaResource::Type type, sp<IResourceManagerClient> *client) { 459 int lowestPriorityPid; 460 int lowestPriority; 461 int callingPriority; 462 if (!mProcessInfo->getPriority(callingPid, &callingPriority)) { 463 ALOGE("getLowestPriorityBiggestClient_l: can't get process priority for pid %d", 464 callingPid); 465 return false; 466 } 467 if (!getLowestPriorityPid_l(type, &lowestPriorityPid, &lowestPriority)) { 468 return false; 469 } 470 if (lowestPriority <= callingPriority) { 471 ALOGE("getLowestPriorityBiggestClient_l: lowest priority %d vs caller priority %d", 472 lowestPriority, callingPriority); 473 return false; 474 } 475 476 if (!getBiggestClient_l(lowestPriorityPid, type, client)) { 477 return false; 478 } 479 return true; 480 } 481 482 bool ResourceManagerService::getLowestPriorityPid_l( 483 MediaResource::Type type, int *lowestPriorityPid, int *lowestPriority) { 484 int pid = -1; 485 int priority = -1; 486 for (size_t i = 0; i < mMap.size(); ++i) { 487 if (mMap.valueAt(i).size() == 0) { 488 // no client on this process. 489 continue; 490 } 491 if (!hasResourceType(type, mMap.valueAt(i))) { 492 // doesn't have the requested resource type 493 continue; 494 } 495 int tempPid = mMap.keyAt(i); 496 int tempPriority; 497 if (!mProcessInfo->getPriority(tempPid, &tempPriority)) { 498 ALOGV("getLowestPriorityPid_l: can't get priority of pid %d, skipped", tempPid); 499 // TODO: remove this pid from mMap? 500 continue; 501 } 502 if (pid == -1 || tempPriority > priority) { 503 // initial the value 504 pid = tempPid; 505 priority = tempPriority; 506 } 507 } 508 if (pid != -1) { 509 *lowestPriorityPid = pid; 510 *lowestPriority = priority; 511 } 512 return (pid != -1); 513 } 514 515 bool ResourceManagerService::isCallingPriorityHigher_l(int callingPid, int pid) { 516 int callingPidPriority; 517 if (!mProcessInfo->getPriority(callingPid, &callingPidPriority)) { 518 return false; 519 } 520 521 int priority; 522 if (!mProcessInfo->getPriority(pid, &priority)) { 523 return false; 524 } 525 526 return (callingPidPriority < priority); 527 } 528 529 bool ResourceManagerService::getBiggestClient_l( 530 int pid, MediaResource::Type type, sp<IResourceManagerClient> *client) { 531 ssize_t index = mMap.indexOfKey(pid); 532 if (index < 0) { 533 ALOGE("getBiggestClient_l: can't find resource info for pid %d", pid); 534 return false; 535 } 536 537 sp<IResourceManagerClient> clientTemp; 538 uint64_t largestValue = 0; 539 const ResourceInfos &infos = mMap.valueAt(index); 540 for (size_t i = 0; i < infos.size(); ++i) { 541 Vector<MediaResource> resources = infos[i].resources; 542 for (size_t j = 0; j < resources.size(); ++j) { 543 if (resources[j].mType == type) { 544 if (resources[j].mValue > largestValue) { 545 largestValue = resources[j].mValue; 546 clientTemp = infos[i].client; 547 } 548 } 549 } 550 } 551 552 if (clientTemp == NULL) { 553 ALOGE("getBiggestClient_l: can't find resource type %s for pid %d", asString(type), pid); 554 return false; 555 } 556 557 *client = clientTemp; 558 return true; 559 } 560 561 } // namespace android 562