1 /* 2 * Copyright (C) 2018 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 //#define LOG_NDEBUG 0 18 #define LOG_TAG "ACameraManagerVendor" 19 20 #include <memory> 21 #include "ndk_vendor/impl/ACameraManager.h" 22 #include "ACameraMetadata.h" 23 #include "ndk_vendor/impl/ACameraDevice.h" 24 #include "utils.h" 25 #include <CameraMetadata.h> 26 #include <camera_metadata_hidden.h> 27 28 #include <utils/Vector.h> 29 #include <cutils/properties.h> 30 #include <stdlib.h> 31 32 #include <VendorTagDescriptor.h> 33 34 using namespace android::acam; 35 36 namespace android { 37 namespace acam { 38 39 using frameworks::cameraservice::service::V2_0::CameraStatusAndId; 40 using frameworks::cameraservice::common::V2_0::ProviderIdAndVendorTagSections; 41 using android::hardware::camera::common::V1_0::helper::VendorTagDescriptor; 42 using android::hardware::camera::common::V1_0::helper::VendorTagDescriptorCache; 43 44 // Static member definitions 45 const char* CameraManagerGlobal::kCameraIdKey = "CameraId"; 46 const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp"; 47 const char* CameraManagerGlobal::kContextKey = "CallbackContext"; 48 Mutex CameraManagerGlobal::sLock; 49 CameraManagerGlobal* CameraManagerGlobal::sInstance = nullptr; 50 51 /** 52 * The vendor tag descriptor class that takes HIDL vendor tag information as 53 * input. Not part of vendor available VendorTagDescriptor class because that class is used by 54 * default HAL implementation code as well. 55 */ 56 class HidlVendorTagDescriptor : public VendorTagDescriptor { 57 public: 58 /** 59 * Create a VendorTagDescriptor object from the HIDL VendorTagSection 60 * vector. 61 * 62 * Returns OK on success, or a negative error code. 63 */ 64 static status_t createDescriptorFromHidl(const hidl_vec<VendorTagSection>& vts, 65 /*out*/ sp<VendorTagDescriptor> *descriptor); 66 }; 67 68 status_t HidlVendorTagDescriptor::createDescriptorFromHidl(const hidl_vec<VendorTagSection> &vts, 69 sp<VendorTagDescriptor> *descriptor) { 70 int tagCount = 0; 71 72 for (size_t s = 0; s < vts.size(); s++) { 73 tagCount += vts[s].tags.size(); 74 } 75 76 if (tagCount < 0 || tagCount > INT32_MAX) { 77 ALOGE("%s: tag count %d from vendor tag sections is invalid.", __FUNCTION__, tagCount); 78 return BAD_VALUE; 79 } 80 81 Vector<uint32_t> tagArray; 82 LOG_ALWAYS_FATAL_IF(tagArray.resize(tagCount) != tagCount, 83 "%s: too many (%u) vendor tags defined.", __FUNCTION__, tagCount); 84 85 sp<HidlVendorTagDescriptor> desc = new HidlVendorTagDescriptor(); 86 desc->mTagCount = tagCount; 87 88 KeyedVector<uint32_t, String8> tagToSectionMap; 89 90 int idx = 0; 91 for (size_t s = 0; s < vts.size(); s++) { 92 const VendorTagSection& section = vts[s]; 93 const char *sectionName = section.sectionName.c_str(); 94 if (sectionName == NULL) { 95 ALOGE("%s: no section name defined for vendor tag section %zu.", __FUNCTION__, s); 96 return BAD_VALUE; 97 } 98 String8 sectionString(sectionName); 99 desc->mSections.add(sectionString); 100 101 for (size_t j = 0; j < section.tags.size(); j++) { 102 uint32_t tag = section.tags[j].tagId; 103 if (tag < CAMERA_METADATA_VENDOR_TAG_BOUNDARY) { 104 ALOGE("%s: vendor tag %d not in vendor tag section.", __FUNCTION__, tag); 105 return BAD_VALUE; 106 } 107 108 tagArray.editItemAt(idx++) = section.tags[j].tagId; 109 110 const char *tagName = section.tags[j].tagName.c_str(); 111 if (tagName == NULL) { 112 ALOGE("%s: no tag name defined for vendor tag %d.", __FUNCTION__, tag); 113 return BAD_VALUE; 114 } 115 desc->mTagToNameMap.add(tag, String8(tagName)); 116 tagToSectionMap.add(tag, sectionString); 117 118 int tagType = (int) section.tags[j].tagType; 119 if (tagType < 0 || tagType >= NUM_TYPES) { 120 ALOGE("%s: tag type %d from vendor ops does not exist.", __FUNCTION__, tagType); 121 return BAD_VALUE; 122 } 123 desc->mTagToTypeMap.emplace(tag, tagType); 124 } 125 } 126 127 for (size_t i = 0; i < tagArray.size(); ++i) { 128 uint32_t tag = tagArray[i]; 129 String8 sectionString = tagToSectionMap.valueFor(tag); 130 131 // Set up tag to section index map 132 ssize_t index = desc->mSections.indexOf(sectionString); 133 LOG_ALWAYS_FATAL_IF(index < 0, "index %zd must be non-negative", index); 134 desc->mTagToSectionMap.add(tag, static_cast<uint32_t>(index)); 135 136 // Set up reverse mapping 137 ssize_t reverseIndex = -1; 138 if ((reverseIndex = desc->mReverseMapping.indexOfKey(sectionString)) < 0) { 139 KeyedVector<String8, uint32_t>* nameMapper = new KeyedVector<String8, uint32_t>(); 140 reverseIndex = desc->mReverseMapping.add(sectionString, nameMapper); 141 } 142 desc->mReverseMapping[reverseIndex]->add(desc->mTagToNameMap.valueFor(tag), tag); 143 } 144 145 *descriptor = std::move(desc); 146 return OK; 147 } 148 149 CameraManagerGlobal& 150 CameraManagerGlobal::getInstance() { 151 Mutex::Autolock _l(sLock); 152 CameraManagerGlobal* instance = sInstance; 153 if (instance == nullptr) { 154 instance = new CameraManagerGlobal(); 155 sInstance = instance; 156 } 157 return *instance; 158 } 159 160 CameraManagerGlobal::~CameraManagerGlobal() { 161 // clear sInstance so next getInstance call knows to create a new one 162 Mutex::Autolock _sl(sLock); 163 sInstance = nullptr; 164 Mutex::Autolock _l(mLock); 165 if (mCameraService != nullptr) { 166 mCameraService->unlinkToDeath(mDeathNotifier); 167 mCameraService->removeListener(mCameraServiceListener); 168 } 169 mDeathNotifier.clear(); 170 if (mCbLooper != nullptr) { 171 mCbLooper->unregisterHandler(mHandler->id()); 172 mCbLooper->stop(); 173 } 174 mCbLooper.clear(); 175 mHandler.clear(); 176 mCameraServiceListener.clear(); 177 mCameraService.clear(); 178 } 179 180 static bool isCameraServiceDisabled() { 181 char value[PROPERTY_VALUE_MAX]; 182 property_get("config.disable_cameraservice", value, "0"); 183 return (strncmp(value, "0", 2) != 0 && strncasecmp(value, "false", 6) != 0); 184 } 185 186 bool CameraManagerGlobal::setupVendorTags() { 187 sp<VendorTagDescriptorCache> tagCache = new VendorTagDescriptorCache(); 188 Status status = Status::NO_ERROR; 189 std::vector<ProviderIdAndVendorTagSections> providerIdsAndVts; 190 auto remoteRet = mCameraService->getCameraVendorTagSections([&status, &providerIdsAndVts] 191 (Status s, 192 auto &IdsAndVts) { 193 status = s; 194 providerIdsAndVts = IdsAndVts; }); 195 196 if (!remoteRet.isOk() || status != Status::NO_ERROR) { 197 ALOGE("Failed to retrieve VendorTagSections %s", remoteRet.description().c_str()); 198 return false; 199 } 200 // Convert each providers VendorTagSections into a VendorTagDescriptor and 201 // add it to the cache 202 for (auto &providerIdAndVts : providerIdsAndVts) { 203 sp<VendorTagDescriptor> vendorTagDescriptor; 204 if (HidlVendorTagDescriptor::createDescriptorFromHidl(providerIdAndVts.vendorTagSections, 205 &vendorTagDescriptor) != OK) { 206 ALOGE("Failed to convert from Hidl: VendorTagDescriptor"); 207 return false; 208 } 209 tagCache->addVendorDescriptor(providerIdAndVts.providerId, vendorTagDescriptor); 210 } 211 VendorTagDescriptorCache::setAsGlobalVendorTagCache(tagCache); 212 return true; 213 } 214 215 sp<ICameraService> CameraManagerGlobal::getCameraService() { 216 Mutex::Autolock _l(mLock); 217 if (mCameraService.get() == nullptr) { 218 if (isCameraServiceDisabled()) { 219 return mCameraService; 220 } 221 222 sp<ICameraService> cameraServiceBinder; 223 do { 224 cameraServiceBinder = ICameraService::getService(); 225 if (cameraServiceBinder != nullptr) { 226 break; 227 } 228 ALOGW("CameraService not published, waiting..."); 229 usleep(kCameraServicePollDelay); 230 } while(true); 231 if (mDeathNotifier == nullptr) { 232 mDeathNotifier = new DeathNotifier(this); 233 } 234 cameraServiceBinder->linkToDeath(mDeathNotifier, 0); 235 mCameraService = cameraServiceBinder; 236 237 // Setup looper thread to perfrom availiability callbacks 238 if (mCbLooper == nullptr) { 239 mCbLooper = new ALooper; 240 mCbLooper->setName("C2N-mgr-looper"); 241 status_t err = mCbLooper->start( 242 /*runOnCallingThread*/false, 243 /*canCallJava*/ true, 244 PRIORITY_DEFAULT); 245 if (err != OK) { 246 ALOGE("%s: Unable to start camera service listener looper: %s (%d)", 247 __FUNCTION__, strerror(-err), err); 248 mCbLooper.clear(); 249 return nullptr; 250 } 251 if (mHandler == nullptr) { 252 mHandler = new CallbackHandler(); 253 } 254 mCbLooper->registerHandler(mHandler); 255 } 256 257 // register ICameraServiceListener 258 if (mCameraServiceListener == nullptr) { 259 mCameraServiceListener = new CameraServiceListener(this); 260 } 261 hidl_vec<CameraStatusAndId> cameraStatuses{}; 262 Status status = Status::NO_ERROR; 263 auto remoteRet = mCameraService->addListener(mCameraServiceListener, 264 [&status, &cameraStatuses](Status s, 265 auto &retStatuses) { 266 status = s; 267 cameraStatuses = retStatuses; 268 }); 269 if (!remoteRet.isOk() || status != Status::NO_ERROR) { 270 ALOGE("Failed to add listener to camera service %s", remoteRet.description().c_str()); 271 } 272 273 // Setup vendor tags 274 if (!setupVendorTags()) { 275 ALOGE("Unable to set up vendor tags"); 276 return nullptr; 277 } 278 279 for (auto& c : cameraStatuses) { 280 onStatusChangedLocked(c); 281 } 282 } 283 return mCameraService; 284 } 285 286 void CameraManagerGlobal::DeathNotifier::serviceDied(uint64_t cookie, const wp<IBase> &who) { 287 (void) cookie; 288 (void) who; 289 ALOGE("Camera service binderDied!"); 290 sp<CameraManagerGlobal> cm = mCameraManager.promote(); 291 if (cm != nullptr) { 292 AutoMutex lock(cm->mLock); 293 for (auto& pair : cm->mDeviceStatusMap) { 294 CameraStatusAndId cameraStatusAndId; 295 cameraStatusAndId.cameraId = pair.first; 296 cameraStatusAndId.deviceStatus = pair.second; 297 cm->onStatusChangedLocked(cameraStatusAndId); 298 } 299 cm->mCameraService.clear(); 300 // TODO: consider adding re-connect call here? 301 } 302 } 303 304 void CameraManagerGlobal::registerAvailabilityCallback( 305 const ACameraManager_AvailabilityCallbacks *callback) { 306 Mutex::Autolock _l(mLock); 307 Callback cb(callback); 308 auto pair = mCallbacks.insert(cb); 309 // Send initial callbacks if callback is newly registered 310 if (pair.second) { 311 for (auto& pair : mDeviceStatusMap) { 312 const hidl_string& cameraId = pair.first; 313 CameraDeviceStatus status = pair.second; 314 315 sp<AMessage> msg = new AMessage(kWhatSendSingleCallback, mHandler); 316 ACameraManager_AvailabilityCallback cb = isStatusAvailable(status) ? 317 callback->onCameraAvailable : callback->onCameraUnavailable; 318 msg->setPointer(kCallbackFpKey, (void *) cb); 319 msg->setPointer(kContextKey, callback->context); 320 msg->setString(kCameraIdKey, AString(cameraId.c_str())); 321 msg->post(); 322 } 323 } 324 } 325 326 void CameraManagerGlobal::unregisterAvailabilityCallback( 327 const ACameraManager_AvailabilityCallbacks *callback) { 328 Mutex::Autolock _l(mLock); 329 Callback cb(callback); 330 mCallbacks.erase(cb); 331 } 332 333 void CameraManagerGlobal::getCameraIdList(std::vector<hidl_string>* cameraIds) { 334 // Ensure that we have initialized/refreshed the list of available devices 335 auto cs = getCameraService(); 336 Mutex::Autolock _l(mLock); 337 338 for(auto& deviceStatus : mDeviceStatusMap) { 339 if (deviceStatus.second == CameraDeviceStatus::STATUS_NOT_PRESENT || 340 deviceStatus.second == CameraDeviceStatus::STATUS_ENUMERATING) { 341 continue; 342 } 343 cameraIds->push_back(deviceStatus.first); 344 } 345 } 346 347 bool CameraManagerGlobal::validStatus(CameraDeviceStatus status) { 348 switch (status) { 349 case CameraDeviceStatus::STATUS_NOT_PRESENT: 350 case CameraDeviceStatus::STATUS_PRESENT: 351 case CameraDeviceStatus::STATUS_ENUMERATING: 352 case CameraDeviceStatus::STATUS_NOT_AVAILABLE: 353 return true; 354 default: 355 return false; 356 } 357 } 358 359 bool CameraManagerGlobal::isStatusAvailable(CameraDeviceStatus status) { 360 switch (status) { 361 case CameraDeviceStatus::STATUS_PRESENT: 362 return true; 363 default: 364 return false; 365 } 366 } 367 368 void CameraManagerGlobal::CallbackHandler::onMessageReceived( 369 const sp<AMessage> &msg) { 370 switch (msg->what()) { 371 case kWhatSendSingleCallback: 372 { 373 ACameraManager_AvailabilityCallback cb; 374 void* context; 375 AString cameraId; 376 bool found = msg->findPointer(kCallbackFpKey, (void**) &cb); 377 if (!found) { 378 ALOGE("%s: Cannot find camera callback fp!", __FUNCTION__); 379 return; 380 } 381 found = msg->findPointer(kContextKey, &context); 382 if (!found) { 383 ALOGE("%s: Cannot find callback context!", __FUNCTION__); 384 return; 385 } 386 found = msg->findString(kCameraIdKey, &cameraId); 387 if (!found) { 388 ALOGE("%s: Cannot find camera ID!", __FUNCTION__); 389 return; 390 } 391 (*cb)(context, cameraId.c_str()); 392 break; 393 } 394 default: 395 ALOGE("%s: unknown message type %d", __FUNCTION__, msg->what()); 396 break; 397 } 398 } 399 400 hardware::Return<void> CameraManagerGlobal::CameraServiceListener::onStatusChanged( 401 const CameraStatusAndId &statusAndId) { 402 sp<CameraManagerGlobal> cm = mCameraManager.promote(); 403 if (cm != nullptr) { 404 cm->onStatusChanged(statusAndId); 405 } else { 406 ALOGE("Cannot deliver status change. Global camera manager died"); 407 } 408 return Void(); 409 } 410 411 void CameraManagerGlobal::onStatusChanged( 412 const CameraStatusAndId &statusAndId) { 413 Mutex::Autolock _l(mLock); 414 onStatusChangedLocked(statusAndId); 415 } 416 417 void CameraManagerGlobal::onStatusChangedLocked( 418 const CameraStatusAndId &statusAndId) { 419 hidl_string cameraId = statusAndId.cameraId; 420 CameraDeviceStatus status = statusAndId.deviceStatus; 421 if (!validStatus(status)) { 422 ALOGE("%s: Invalid status %d", __FUNCTION__, status); 423 return; 424 } 425 426 bool firstStatus = (mDeviceStatusMap.count(cameraId) == 0); 427 CameraDeviceStatus oldStatus = firstStatus ? 428 status : // first status 429 mDeviceStatusMap[cameraId]; 430 431 if (!firstStatus && 432 isStatusAvailable(status) == isStatusAvailable(oldStatus)) { 433 // No status update. No need to send callback 434 return; 435 } 436 437 // Iterate through all registered callbacks 438 mDeviceStatusMap[cameraId] = status; 439 for (auto cb : mCallbacks) { 440 sp<AMessage> msg = new AMessage(kWhatSendSingleCallback, mHandler); 441 ACameraManager_AvailabilityCallback cbFp = isStatusAvailable(status) ? 442 cb.mAvailable : cb.mUnavailable; 443 msg->setPointer(kCallbackFpKey, (void *) cbFp); 444 msg->setPointer(kContextKey, cb.mContext); 445 msg->setString(kCameraIdKey, AString(cameraId.c_str())); 446 msg->post(); 447 } 448 if (status == CameraDeviceStatus::STATUS_NOT_PRESENT) { 449 mDeviceStatusMap.erase(cameraId); 450 } 451 } 452 453 } // namespace acam 454 } // namespace android 455 456 /** 457 * ACameraManger Implementation 458 */ 459 camera_status_t 460 ACameraManager::getCameraIdList(ACameraIdList** cameraIdList) { 461 Mutex::Autolock _l(mLock); 462 463 std::vector<hidl_string> idList; 464 CameraManagerGlobal::getInstance().getCameraIdList(&idList); 465 466 int numCameras = idList.size(); 467 ACameraIdList *out = new ACameraIdList; 468 if (!out) { 469 ALOGE("Allocate memory for ACameraIdList failed!"); 470 return ACAMERA_ERROR_NOT_ENOUGH_MEMORY; 471 } 472 out->numCameras = numCameras; 473 out->cameraIds = new const char*[numCameras]; 474 if (!out->cameraIds) { 475 ALOGE("Allocate memory for ACameraIdList failed!"); 476 deleteCameraIdList(out); 477 return ACAMERA_ERROR_NOT_ENOUGH_MEMORY; 478 } 479 for (int i = 0; i < numCameras; i++) { 480 const char* src = idList[i].c_str(); 481 size_t dstSize = strlen(src) + 1; 482 char* dst = new char[dstSize]; 483 if (!dst) { 484 ALOGE("Allocate memory for ACameraIdList failed!"); 485 deleteCameraIdList(out); 486 return ACAMERA_ERROR_NOT_ENOUGH_MEMORY; 487 } 488 strlcpy(dst, src, dstSize); 489 out->cameraIds[i] = dst; 490 } 491 *cameraIdList = out; 492 return ACAMERA_OK; 493 } 494 495 void 496 ACameraManager::deleteCameraIdList(ACameraIdList* cameraIdList) { 497 if (cameraIdList != nullptr) { 498 if (cameraIdList->cameraIds != nullptr) { 499 for (int i = 0; i < cameraIdList->numCameras; i ++) { 500 if (cameraIdList->cameraIds[i] != nullptr) { 501 delete[] cameraIdList->cameraIds[i]; 502 } 503 } 504 delete[] cameraIdList->cameraIds; 505 } 506 delete cameraIdList; 507 } 508 } 509 510 camera_status_t ACameraManager::getCameraCharacteristics( 511 const char *cameraIdStr, sp<ACameraMetadata> *characteristics) { 512 Mutex::Autolock _l(mLock); 513 514 sp<ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService(); 515 if (cs == nullptr) { 516 ALOGE("%s: Cannot reach camera service!", __FUNCTION__); 517 return ACAMERA_ERROR_CAMERA_DISCONNECTED; 518 } 519 CameraMetadata rawMetadata; 520 Status status = Status::NO_ERROR; 521 auto serviceRet = 522 cs->getCameraCharacteristics(cameraIdStr, 523 [&status, &rawMetadata] (auto s , 524 const hidl_vec<uint8_t> &metadata) { 525 status = s; 526 if (status == Status::NO_ERROR) { 527 utils::convertFromHidlCloned(metadata, &rawMetadata); 528 } 529 }); 530 if (!serviceRet.isOk() || status != Status::NO_ERROR) { 531 ALOGE("Get camera characteristics from camera service failed"); 532 return ACAMERA_ERROR_UNKNOWN; // should not reach here 533 } 534 535 *characteristics = new ACameraMetadata( 536 rawMetadata.release(), ACameraMetadata::ACM_CHARACTERISTICS); 537 return ACAMERA_OK; 538 } 539 540 camera_status_t 541 ACameraManager::openCamera( 542 const char* cameraId, 543 ACameraDevice_StateCallbacks* callback, 544 /*out*/ACameraDevice** outDevice) { 545 sp<ACameraMetadata> rawChars; 546 camera_status_t ret = getCameraCharacteristics(cameraId, &rawChars); 547 Mutex::Autolock _l(mLock); 548 if (ret != ACAMERA_OK) { 549 ALOGE("%s: cannot get camera characteristics for camera %s. err %d", 550 __FUNCTION__, cameraId, ret); 551 return ACAMERA_ERROR_INVALID_PARAMETER; 552 } 553 554 ACameraDevice* device = new ACameraDevice(cameraId, callback, std::move(rawChars)); 555 556 sp<ICameraService> cs = CameraManagerGlobal::getInstance().getCameraService(); 557 if (cs == nullptr) { 558 ALOGE("%s: Cannot reach camera service!", __FUNCTION__); 559 delete device; 560 return ACAMERA_ERROR_CAMERA_DISCONNECTED; 561 } 562 563 sp<ICameraDeviceCallback> callbacks = device->getServiceCallback(); 564 sp<ICameraDeviceUser> deviceRemote; 565 566 // No way to get package name from native. 567 // Send a zero length package name and let camera service figure it out from UID 568 Status status = Status::NO_ERROR; 569 auto serviceRet = cs->connectDevice( 570 callbacks, cameraId, [&status, &deviceRemote](auto s, auto &device) { 571 status = s; 572 deviceRemote = device; 573 }); 574 575 if (!serviceRet.isOk() || status != Status::NO_ERROR) { 576 ALOGE("%s: connect camera device failed", __FUNCTION__); 577 // TODO: Convert serviceRet to camera_status_t 578 delete device; 579 return ACAMERA_ERROR_UNKNOWN; 580 } 581 if (deviceRemote == nullptr) { 582 ALOGE("%s: connect camera device failed! remote device is null", __FUNCTION__); 583 delete device; 584 return ACAMERA_ERROR_CAMERA_DISCONNECTED; 585 } 586 device->setRemoteDevice(deviceRemote); 587 device->setDeviceMetadataQueues(); 588 *outDevice = device; 589 return ACAMERA_OK; 590 } 591 592 camera_status_t 593 ACameraManager::getTagFromName(const char *cameraId, const char *name, uint32_t *tag) { 594 sp<ACameraMetadata> rawChars; 595 camera_status_t ret = getCameraCharacteristics(cameraId, &rawChars); 596 if (ret != ACAMERA_OK) { 597 ALOGE("%s, Cannot retrieve camera characteristics for camera id %s", __FUNCTION__, 598 cameraId); 599 return ACAMERA_ERROR_METADATA_NOT_FOUND; 600 } 601 const CameraMetadata& metadata = rawChars->getInternalData(); 602 const camera_metadata_t *rawMetadata = metadata.getAndLock(); 603 metadata_vendor_id_t vendorTagId = get_camera_metadata_vendor_id(rawMetadata); 604 metadata.unlock(rawMetadata); 605 sp<VendorTagDescriptorCache> vtCache = VendorTagDescriptorCache::getGlobalVendorTagCache(); 606 sp<VendorTagDescriptor> vTags = nullptr; 607 vtCache->getVendorTagDescriptor(vendorTagId, &vTags); 608 status_t status= metadata.getTagFromName(name, vTags.get(), tag); 609 return status == OK ? ACAMERA_OK : ACAMERA_ERROR_METADATA_NOT_FOUND; 610 } 611 612 ACameraManager::~ACameraManager() { 613 614 } 615