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