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