From cc776718c0be7c31fe5ab4fc1446d377be60369f Mon Sep 17 00:00:00 2001 From: Ruben Brunk Date: Tue, 17 Feb 2015 20:18:47 -0800 Subject: camera2: Add camera client eviction enforcement. - This updates the CameraService to implement client eviction behavior based on process priority. Bug: 19186859 Change-Id: I646939b1cdf1a2237c4e5044164d55a2542cf36e --- camera/CameraParameters.cpp | 4 + include/camera/CameraParameters.h | 3 + services/camera/libcameraservice/Android.mk | 1 + .../libcameraservice/CameraDeviceFactory.cpp | 1 + .../camera/libcameraservice/CameraFlashlight.cpp | 14 + services/camera/libcameraservice/CameraService.cpp | 1654 ++++++++++---------- services/camera/libcameraservice/CameraService.h | 543 +++++-- .../camera/libcameraservice/api1/Camera2Client.cpp | 4 +- .../camera/libcameraservice/api1/CameraClient.cpp | 51 +- .../libcameraservice/api2/CameraDeviceClient.cpp | 4 +- .../libcameraservice/utils/AutoConditionLock.cpp | 90 ++ .../libcameraservice/utils/AutoConditionLock.h | 99 ++ .../camera/libcameraservice/utils/ClientManager.h | 539 +++++++ .../camera/libcameraservice/utils/RingBuffer.h | 361 +++++ 14 files changed, 2427 insertions(+), 941 deletions(-) create mode 100644 services/camera/libcameraservice/utils/AutoConditionLock.cpp create mode 100644 services/camera/libcameraservice/utils/AutoConditionLock.h create mode 100644 services/camera/libcameraservice/utils/ClientManager.h create mode 100644 services/camera/libcameraservice/utils/RingBuffer.h diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp index 3dbf75e..68969cf 100644 --- a/camera/CameraParameters.cpp +++ b/camera/CameraParameters.cpp @@ -530,4 +530,8 @@ int CameraParameters::previewFormatToEnum(const char* format) { -1; } +bool CameraParameters::isEmpty() const { + return mMap.isEmpty(); +} + }; // namespace android diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h index c6074fc..ba33ffe 100644 --- a/include/camera/CameraParameters.h +++ b/include/camera/CameraParameters.h @@ -108,6 +108,9 @@ public: */ void getSupportedPreviewFormats(Vector& formats) const; + // Returns true if no keys are present + bool isEmpty() const; + // Parameter keys to communicate between camera application and driver. // The access (read/write, read only, or write only) is viewed from the // perspective of applications, not driver. diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 5d6423a..de841c8 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -54,6 +54,7 @@ LOCAL_SRC_FILES:= \ device3/StatusTracker.cpp \ gui/RingBufferConsumer.cpp \ utils/CameraTraces.cpp \ + utils/AutoConditionLock.cpp \ LOCAL_SHARED_LIBRARIES:= \ libui \ diff --git a/services/camera/libcameraservice/CameraDeviceFactory.cpp b/services/camera/libcameraservice/CameraDeviceFactory.cpp index bfef50e..6589e27 100644 --- a/services/camera/libcameraservice/CameraDeviceFactory.cpp +++ b/services/camera/libcameraservice/CameraDeviceFactory.cpp @@ -48,6 +48,7 @@ sp CameraDeviceFactory::createDevice(int cameraId) { case CAMERA_DEVICE_API_VERSION_3_0: case CAMERA_DEVICE_API_VERSION_3_1: case CAMERA_DEVICE_API_VERSION_3_2: + case CAMERA_DEVICE_API_VERSION_3_3: device = new Camera3Device(cameraId); break; default: diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp index 8e894cd..6c237aa 100644 --- a/services/camera/libcameraservice/CameraFlashlight.cpp +++ b/services/camera/libcameraservice/CameraFlashlight.cpp @@ -110,6 +110,20 @@ status_t CameraFlashlight::setTorchMode(const String8& cameraId, bool enabled) { status_t res = OK; Mutex::Autolock l(mLock); + if (mOpenedCameraIds.indexOf(cameraId) != NAME_NOT_FOUND) { + // This case is needed to avoid state corruption during the following call sequence: + // CameraService::setTorchMode for camera ID 0 begins, does torch status checks + // CameraService::connect for camera ID 0 begins, calls prepareDeviceOpen, ends + // CameraService::setTorchMode for camera ID 0 continues, calls + // CameraFlashlight::setTorchMode + + // TODO: Move torch status checks and state updates behind this CameraFlashlight lock + // to avoid other similar race conditions. + ALOGE("%s: Camera device %s is in use, cannot set torch mode.", + __FUNCTION__, cameraId.string()); + return -EBUSY; + } + if (mFlashControl == NULL) { if (enabled == false) { return OK; diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 6f37f16..574165b 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -17,9 +17,14 @@ #define LOG_TAG "CameraService" //#define LOG_NDEBUG 0 +#include +#include #include -#include +#include +#include +#include #include +#include #include #include @@ -27,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -67,25 +73,16 @@ static void setLogLevel(int level) { // ---------------------------------------------------------------------------- -static int getCallingPid() { - return IPCThreadState::self()->getCallingPid(); -} - -static int getCallingUid() { - return IPCThreadState::self()->getCallingUid(); -} - extern "C" { static void camera_device_status_change( const struct camera_module_callbacks* callbacks, int camera_id, int new_status) { sp cs = const_cast( - static_cast(callbacks)); + static_cast(callbacks)); - cs->onDeviceStatusChanged( - camera_id, - new_status); + cs->onDeviceStatusChanged(static_cast(camera_id), + static_cast(new_status)); } static void torch_mode_status_change( @@ -128,23 +125,20 @@ static void torch_mode_status_change( static CameraService *gCameraService; CameraService::CameraService() - :mSoundRef(0), mModule(0), mFlashlight(0) + : mEventLog(DEFAULT_EVICTION_LOG_LENGTH), mSoundRef(0), mModule(0), mFlashlight(0) { ALOGI("CameraService started (pid=%d)", getpid()); gCameraService = this; - for (size_t i = 0; i < MAX_CAMERAS; ++i) { - mStatusList[i] = ICameraServiceListener::STATUS_PRESENT; - } - this->camera_device_status_change = android::camera_device_status_change; this->torch_mode_status_change = android::torch_mode_status_change; + mServiceLockWrapper = std::make_shared(&mServiceLock); } void CameraService::onFirstRef() { - LOG1("CameraService::onFirstRef"); + ALOGI("CameraService process starting"); BnCameraService::onFirstRef(); @@ -157,27 +151,53 @@ void CameraService::onFirstRef() else { mModule = new CameraModule(rawModule); const hw_module_t *common = mModule->getRawModule(); - ALOGI("Loaded \"%s\" cameraCa module", common->name); + ALOGI("Loaded \"%s\" camera module", common->name); mNumberOfCameras = mModule->getNumberOfCameras(); - if (mNumberOfCameras > MAX_CAMERAS) { - ALOGE("Number of cameras(%d) > MAX_CAMERAS(%d).", - mNumberOfCameras, MAX_CAMERAS); - mNumberOfCameras = MAX_CAMERAS; - } mFlashlight = new CameraFlashlight(*mModule, *this); status_t res = mFlashlight->findFlashUnits(); if (res) { // impossible because we haven't open any camera devices. - ALOGE("failed to find flash units."); + ALOGE("Failed to find flash units."); } for (int i = 0; i < mNumberOfCameras; i++) { - setCameraFree(i); + String8 cameraId = String8::format("%d", i); + + // Defaults to use for cost and conflicting devices + int cost = 100; + char** conflicting_devices = nullptr; + size_t conflicting_devices_length = 0; + + // If using post-2.4 module version, query the cost + conflicting devices from the HAL + if (common->module_api_version >= CAMERA_MODULE_API_VERSION_2_4) { + struct camera_info info; + status_t rc = mModule->getCameraInfo(i, &info); + if (rc == NO_ERROR) { + cost = info.resource_cost; + conflicting_devices = info.conflicting_devices; + conflicting_devices_length = info.conflicting_devices_length; + } else { + ALOGE("%s: Received error loading camera info for device %d, cost and" + " conflicting devices fields set to defaults for this device.", + __FUNCTION__, i); + } + } + + std::set conflicting; + for (size_t i = 0; i < conflicting_devices_length; i++) { + conflicting.emplace(String8(conflicting_devices[i])); + } + + // Initialize state for each camera device + { + Mutex::Autolock lock(mCameraStatesLock); + mCameraStates.emplace(cameraId, std::make_shared(cameraId, cost, + conflicting)); + } - String8 cameraName = String8::format("%d", i); - if (mFlashlight->hasFlashUnit(cameraName)) { - mTorchStatusMap.add(cameraName, + if (mFlashlight->hasFlashUnit(cameraId)) { + mTorchStatusMap.add(cameraId, ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF); } } @@ -197,80 +217,69 @@ void CameraService::onFirstRef() } CameraService::~CameraService() { - for (int i = 0; i < mNumberOfCameras; i++) { - if (mBusy[i]) { - ALOGE("camera %d is still in use in destructor!", i); - } - } - if (mModule) { delete mModule; + mModule = nullptr; } VendorTagDescriptor::clearGlobalVendorTagDescriptor(); - gCameraService = NULL; + gCameraService = nullptr; } -void CameraService::onDeviceStatusChanged(int cameraId, - int newStatus) -{ - ALOGV("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__, +void CameraService::onDeviceStatusChanged(camera_device_status_t cameraId, + camera_device_status_t newStatus) { + ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__, cameraId, newStatus); - if (cameraId < 0 || cameraId >= MAX_CAMERAS) { + String8 id = String8::format("%d", cameraId); + std::shared_ptr state = getCameraState(id); + + if (state == nullptr) { ALOGE("%s: Bad camera ID %d", __FUNCTION__, cameraId); return; } - if ((int)getStatus(cameraId) == newStatus) { - ALOGE("%s: State transition to the same status 0x%x not allowed", - __FUNCTION__, (uint32_t)newStatus); + ICameraServiceListener::Status oldStatus = state->getStatus(); + + if (oldStatus == static_cast(newStatus)) { + ALOGE("%s: State transition to the same status %#x not allowed", __FUNCTION__, newStatus); return; } - /* don't do this in updateStatus - since it is also called from connect and we could get into a deadlock */ if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) { - Vector > clientsToDisconnect; + sp clientToDisconnect; { - Mutex::Autolock al(mServiceLock); - - /* Remove cached parameters from shim cache */ - mShimParams.removeItem(cameraId); - - /* Find all clients that we need to disconnect */ - sp client = mClient[cameraId].promote(); - if (client.get() != NULL) { - clientsToDisconnect.push_back(client); - } - - int i = cameraId; - for (size_t j = 0; j < mProClientList[i].size(); ++j) { - sp cl = mProClientList[i][j].promote(); - if (cl != NULL) { - clientsToDisconnect.push_back(cl); - } - } - } + // Don't do this in updateStatus to avoid deadlock over mServiceLock + Mutex::Autolock lock(mServiceLock); - /* now disconnect them. don't hold the lock - or we can get into a deadlock */ + // Set the device status to NOT_PRESENT, clients will no longer be able to connect + // to this device until the status changes + updateStatus(ICameraServiceListener::STATUS_NOT_PRESENT, id); - for (size_t i = 0; i < clientsToDisconnect.size(); ++i) { - sp client = clientsToDisconnect[i]; + // Remove cached shim parameters + state->setShimParams(CameraParameters()); - client->disconnect(); - /** - * The remote app will no longer be able to call methods on the - * client since the client PID will be reset to 0 - */ + // Remove the client from the list of active clients + clientToDisconnect = removeClientLocked(id); + + // Notify the client of disconnection + clientToDisconnect->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, + CaptureResultExtras{}); } - ALOGV("%s: After unplug, disconnected %zu clients", - __FUNCTION__, clientsToDisconnect.size()); - } + ALOGI("%s: Client for camera ID %s evicted due to device status change from HAL", + __FUNCTION__, id.string()); - updateStatus( - static_cast(newStatus), cameraId); + // Disconnect client + if (clientToDisconnect.get() != nullptr) { + // Ensure not in binder RPC so client disconnect PID checks work correctly + LOG_ALWAYS_FATAL_IF(getCallingPid() != getpid(), + "onDeviceStatusChanged must be called from the camera service process!"); + clientToDisconnect->disconnect(); + } + + } else { + updateStatus(static_cast(newStatus), id); + } } @@ -304,9 +313,11 @@ void CameraService::onTorchStatusChangedLocked(const String8& cameraId, return; } - Vector >::const_iterator it; - for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { - (*it)->onTorchStatusChanged(newStatus, String16(cameraId.string())); + { + Mutex::Autolock lock(mStatusListenerLock); + for (auto& i : mListenerList) { + i->onTorchStatusChanged(newStatus, String16{cameraId}); + } } } @@ -333,6 +344,15 @@ status_t CameraService::getCameraInfo(int cameraId, return rc; } +int CameraService::cameraIdToInt(const String8& cameraId) { + errno = 0; + size_t pos = 0; + int ret = stoi(std::string{cameraId.string()}, &pos); + if (errno != 0 || pos != cameraId.size()) { + return -1; + } + return ret; +} status_t CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) { status_t ret = OK; @@ -466,6 +486,54 @@ status_t CameraService::getCameraCharacteristics(int cameraId, return ret; } +int CameraService::getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + +int CameraService::getCallingUid() { + return IPCThreadState::self()->getCallingUid(); +} + +String8 CameraService::getFormattedCurrentTime() { + time_t now = time(nullptr); + char formattedTime[64]; + strftime(formattedTime, sizeof(formattedTime), "%m-%d %H:%M:%S", localtime(&now)); + return String8(formattedTime); +} + +int CameraService::getCameraPriorityFromProcState(int procState) { + // Find the priority for the camera usage based on the process state. Higher priority clients + // win for evictions. + // Note: Unlike the ordering for ActivityManager, persistent system processes will always lose + // the camera to the top/foreground applications. + switch(procState) { + case PROCESS_STATE_TOP: // User visible + return 100; + case PROCESS_STATE_IMPORTANT_FOREGROUND: // Foreground + return 90; + case PROCESS_STATE_PERSISTENT: // Persistent system services + case PROCESS_STATE_PERSISTENT_UI: + return 80; + case PROCESS_STATE_IMPORTANT_BACKGROUND: // "Important" background processes + return 70; + case PROCESS_STATE_BACKUP: // Everything else + case PROCESS_STATE_HEAVY_WEIGHT: + case PROCESS_STATE_SERVICE: + case PROCESS_STATE_RECEIVER: + case PROCESS_STATE_HOME: + case PROCESS_STATE_LAST_ACTIVITY: + case PROCESS_STATE_CACHED_ACTIVITY: + case PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + case PROCESS_STATE_CACHED_EMPTY: + return 1; + case PROCESS_STATE_NONEXISTENT: + return -1; + default: + ALOGE("%s: Received unknown process state from ActivityManagerService!", __FUNCTION__); + return -1; + } +} + status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp& desc) { if (!mModule) { ALOGE("%s: camera hardware module doesn't exist", __FUNCTION__); @@ -545,54 +613,90 @@ bool CameraService::setUpVendorTags() { return true; } -status_t CameraService::initializeShimMetadata(int cameraId) { - int pid = getCallingPid(); - int uid = getCallingUid(); - status_t ret = validateConnect(cameraId, uid); - if (ret != OK) { - // Error already logged by callee - return ret; - } +status_t CameraService::makeClient(const sp& cameraService, + const sp& cameraCb, const String16& packageName, const String8& cameraId, + int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode, + int halVersion, int deviceVersion, apiLevel effectiveApiLevel, + /*out*/sp* client) { - bool needsNewClient = false; - sp client; + // TODO: Update CameraClients + HAL interface to use strings for Camera IDs + int id = cameraIdToInt(cameraId); + if (id == -1) { + ALOGE("%s: Invalid camera ID %s, cannot convert to integer.", __FUNCTION__, + cameraId.string()); + return BAD_VALUE; + } - String16 internalPackageName("media"); - { // Scope for service lock - Mutex::Autolock lock(mServiceLock); - if (mClient[cameraId] != NULL) { - client = static_cast(mClient[cameraId].promote().get()); - } - if (client == NULL) { - needsNewClient = true; - ret = connectHelperLocked(/*out*/client, - /*cameraClient*/NULL, // Empty binder callbacks - cameraId, - internalPackageName, - uid, - pid); - - if (ret != OK) { - // Error already logged by callee - return ret; + if (halVersion < 0 || halVersion == deviceVersion) { + // Default path: HAL version is unspecified by caller, create CameraClient + // based on device version reported by the HAL. + switch(deviceVersion) { + case CAMERA_DEVICE_API_VERSION_1_0: + if (effectiveApiLevel == API_1) { // Camera1 API route + sp tmp = static_cast(cameraCb.get()); + *client = new CameraClient(cameraService, tmp, packageName, id, facing, + clientPid, clientUid, getpid(), legacyMode); + } else { // Camera2 API route + ALOGW("Camera using old HAL version: %d", deviceVersion); + return -EOPNOTSUPP; + } + break; + case CAMERA_DEVICE_API_VERSION_2_0: + case CAMERA_DEVICE_API_VERSION_2_1: + case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: + case CAMERA_DEVICE_API_VERSION_3_3: + if (effectiveApiLevel == API_1) { // Camera1 API route + sp tmp = static_cast(cameraCb.get()); + *client = new Camera2Client(cameraService, tmp, packageName, id, facing, + clientPid, clientUid, servicePid, legacyMode); + } else { // Camera2 API route + sp tmp = + static_cast(cameraCb.get()); + *client = new CameraDeviceClient(cameraService, tmp, packageName, id, + facing, clientPid, clientUid, servicePid); } + break; + default: + // Should not be reachable + ALOGE("Unknown camera device HAL version: %d", deviceVersion); + return INVALID_OPERATION; } - - if (client == NULL) { - ALOGE("%s: Could not connect to client camera device.", __FUNCTION__); - return BAD_VALUE; + } else { + // A particular HAL version is requested by caller. Create CameraClient + // based on the requested HAL version. + if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 && + halVersion == CAMERA_DEVICE_API_VERSION_1_0) { + // Only support higher HAL version device opened as HAL1.0 device. + sp tmp = static_cast(cameraCb.get()); + *client = new CameraClient(cameraService, tmp, packageName, id, facing, + clientPid, clientUid, servicePid, legacyMode); + } else { + // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet. + ALOGE("Invalid camera HAL version %x: HAL %x device can only be" + " opened as HAL %x device", halVersion, deviceVersion, + CAMERA_DEVICE_API_VERSION_1_0); + return INVALID_OPERATION; } - - String8 rawParams = client->getParameters(); - CameraParameters params(rawParams); - mShimParams.add(cameraId, params); } + return NO_ERROR; +} + +status_t CameraService::initializeShimMetadata(int cameraId) { + int uid = getCallingUid(); - // Close client if one was opened solely for this call - if (needsNewClient) { - client->disconnect(); + String16 internalPackageName("media"); + String8 id = String8::format("%d", cameraId); + status_t ret = NO_ERROR; + sp tmp = nullptr; + if ((ret = connectHelper(sp{nullptr}, id, + static_cast(CAMERA_HAL_API_VERSION_UNSPECIFIED), internalPackageName, uid, API_1, + false, true, tmp)) != NO_ERROR) { + ALOGE("%s: Error %d (%s) initializing shim metadata.", __FUNCTION__, ret, strerror(ret)); + return ret; } - return OK; + return NO_ERROR; } status_t CameraService::getLegacyParametersLazy(int cameraId, @@ -608,42 +712,54 @@ status_t CameraService::getLegacyParametersLazy(int cameraId, return BAD_VALUE; } - ssize_t index = -1; - { // Scope for service lock - Mutex::Autolock lock(mServiceLock); - index = mShimParams.indexOfKey(cameraId); - // Release service lock so initializeShimMetadata can be called correctly. + String8 id = String8::format("%d", cameraId); - if (index >= 0) { - *parameters = mShimParams[index]; + // Check if we already have parameters + { + // Scope for service lock + Mutex::Autolock lock(mServiceLock); + auto cameraState = getCameraState(id); + if (cameraState == nullptr) { + ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string()); + return BAD_VALUE; } - } - - if (index < 0) { - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - ret = initializeShimMetadata(cameraId); - IPCThreadState::self()->restoreCallingIdentity(token); - if (ret != OK) { - // Error already logged by callee - return ret; + CameraParameters p = cameraState->getShimParams(); + if (!p.isEmpty()) { + *parameters = p; + return NO_ERROR; } + } - { // Scope for service lock - Mutex::Autolock lock(mServiceLock); - index = mShimParams.indexOfKey(cameraId); - - LOG_ALWAYS_FATAL_IF(index < 0, "index should have been initialized"); + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + ret = initializeShimMetadata(cameraId); + IPCThreadState::self()->restoreCallingIdentity(token); + if (ret != NO_ERROR) { + // Error already logged by callee + return ret; + } - *parameters = mShimParams[index]; + // Check for parameters again + { + // Scope for service lock + Mutex::Autolock lock(mServiceLock); + auto cameraState = getCameraState(id); + if (cameraState == nullptr) { + ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string()); + return BAD_VALUE; + } + CameraParameters p = cameraState->getShimParams(); + if (!p.isEmpty()) { + *parameters = p; + return NO_ERROR; } } - return OK; + ALOGE("%s: Parameters were not initialized, or were empty. Device may not be present.", + __FUNCTION__); + return INVALID_OPERATION; } -status_t CameraService::validateConnect(int cameraId, - /*inout*/ - int& clientUid) const { +status_t CameraService::validateConnect(const String8& cameraId, /*inout*/int& clientUid) const { int callingPid = getCallingPid(); @@ -652,23 +768,25 @@ status_t CameraService::validateConnect(int cameraId, } else { // We only trust our own process to forward client UIDs if (callingPid != getpid()) { - ALOGE("CameraService::connect X (pid %d) rejected (don't trust clientUid)", + ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)", callingPid); return PERMISSION_DENIED; } } if (!mModule) { - ALOGE("Camera HAL module not loaded"); + ALOGE("CameraService::connect X (PID %d) rejected (camera HAL module not loaded)", + callingPid); return -ENODEV; } - if (cameraId < 0 || cameraId >= mNumberOfCameras) { - ALOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).", - callingPid, cameraId); + if (getCameraState(cameraId) == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid, + cameraId.string()); return -ENODEV; } + // Check device policy for this camera char value[PROPERTY_VALUE_MAX]; char key[PROPERTY_KEY_MAX]; int clientUserId = multiuser_get_user_id(clientUid); @@ -676,142 +794,214 @@ status_t CameraService::validateConnect(int cameraId, property_get(key, value, "0"); if (strcmp(value, "1") == 0) { // Camera is disabled by DevicePolicyManager. - ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid); + ALOGE("CameraService::connect X (PID %d) rejected (camera %s is disabled by device " + "policy)", callingPid, cameraId.string()); return -EACCES; } - ICameraServiceListener::Status currentStatus = getStatus(cameraId); + return checkIfDeviceIsUsable(cameraId); +} + +status_t CameraService::checkIfDeviceIsUsable(const String8& cameraId) const { + auto cameraState = getCameraState(cameraId); + int callingPid = getCallingPid(); + if (cameraState == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid, + cameraId.string()); + return -ENODEV; + } + + ICameraServiceListener::Status currentStatus = cameraState->getStatus(); if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) { - ALOGI("Camera is not plugged in," - " connect X (pid %d) rejected", callingPid); + ALOGE("CameraService::connect X (PID %d) rejected (camera %s is not connected)", + callingPid, cameraId.string()); return -ENODEV; } else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) { - ALOGI("Camera is enumerating," - " connect X (pid %d) rejected", callingPid); + ALOGE("CameraService::connect X (PID %d) rejected, (camera %s is initializing)", + callingPid, cameraId.string()); return -EBUSY; } - // Else don't check for STATUS_NOT_AVAILABLE. - // -- It's done implicitly in canConnectUnsafe /w the mBusy array - return OK; + return NO_ERROR; } -bool CameraService::canConnectUnsafe(int cameraId, - const String16& clientPackageName, - const sp& remoteCallback, - sp &client) { - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); +void CameraService::finishConnectLocked(const sp& client, + const CameraService::DescriptorPtr& desc) { - if (mClient[cameraId] != 0) { - client = mClient[cameraId].promote(); - if (client != 0) { - if (remoteCallback == client->getRemote()) { - LOG1("CameraService::connect X (pid %d) (the same client)", - callingPid); - return true; - } else { - // TODOSC: need to support 1 regular client, - // multiple shared clients here - ALOGW("CameraService::connect X (pid %d) rejected" - " (existing client).", callingPid); - return false; - } + // Make a descriptor for the incoming client + auto clientDescriptor = CameraService::CameraClientManager::makeClientDescriptor(client, desc); + auto evicted = mActiveClientManager.addAndEvict(clientDescriptor); + + logConnected(desc->getKey(), static_cast(desc->getOwnerId()), + String8(client->getPackageName())); + + if (evicted.size() > 0) { + // This should never happen - clients should already have been removed in disconnect + for (auto& i : evicted) { + ALOGE("%s: Invalid state: Client for camera %s was not removed in disconnect", + __FUNCTION__, i->getKey().string()); } - mClient[cameraId].clear(); - } - - /* - mBusy is set to false as the last step of the Client destructor, - after which it is guaranteed that the Client destructor has finished ( - including any inherited destructors) - - We only need this for a Client subclasses since we don't allow - multiple Clents to be opened concurrently, but multiple BasicClient - would be fine - */ - if (mBusy[cameraId]) { - ALOGW("CameraService::connect X (pid %d, \"%s\") rejected" - " (camera %d is still busy).", callingPid, - clientName8.string(), cameraId); - return false; - } - return true; + LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, clients not evicted properly", + __FUNCTION__); + } } -status_t CameraService::connectHelperLocked( +status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid, + apiLevel effectiveApiLevel, const sp& remoteCallback, const String8& packageName, /*out*/ - sp& client, - /*in*/ - const sp& cameraClient, - int cameraId, - const String16& clientPackageName, - int clientUid, - int callingPid, - int halVersion, - bool legacyMode) { + sp* client, + std::shared_ptr>>* partial) { - // give flashlight a chance to close devices if necessary. - mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId)); + status_t ret = NO_ERROR; + std::vector> evictedClients; + DescriptorPtr clientDescriptor; + { + if (effectiveApiLevel == API_1) { + // If we are using API1, any existing client for this camera ID with the same remote + // should be returned rather than evicted to allow MediaRecorder to work properly. + + auto current = mActiveClientManager.get(cameraId); + if (current != nullptr) { + auto clientSp = current->getValue(); + if (clientSp.get() != nullptr) { // should never be needed + if (clientSp->getRemote() == remoteCallback) { + ALOGI("CameraService::connect X (PID %d) (second call from same" + "app binder, returning the same client)", clientPid); + *client = clientSp; + return NO_ERROR; + } + } + } + } - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); + // Return error if the device was unplugged or removed by the HAL for some reason + if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) { + return ret; + } - if (halVersion < 0 || halVersion == deviceVersion) { - // Default path: HAL version is unspecified by caller, create CameraClient - // based on device version reported by the HAL. - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - client = new CameraClient(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid(), legacyMode); - break; - case CAMERA_DEVICE_API_VERSION_2_0: - case CAMERA_DEVICE_API_VERSION_2_1: - case CAMERA_DEVICE_API_VERSION_3_0: - case CAMERA_DEVICE_API_VERSION_3_1: - case CAMERA_DEVICE_API_VERSION_3_2: - client = new Camera2Client(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid(), legacyMode); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); + // Get current active client PIDs + std::vector ownerPids(mActiveClientManager.getAllOwners()); + ownerPids.push_back(clientPid); + + std::vector priorities(ownerPids.size(), PROCESS_STATE_NONEXISTENT); + + // Get priorites of all active PIDs + ProcessInfoService::getProcessStatesFromPids(ownerPids.size(), &ownerPids[0], + /*out*/&priorities[0]); + + // Update all active clients' priorities + std::map pidToPriorityMap; + for (size_t i = 0; i < ownerPids.size() - 1; i++) { + pidToPriorityMap.emplace(ownerPids[i], getCameraPriorityFromProcState(priorities[i])); + } + mActiveClientManager.updatePriorities(pidToPriorityMap); + + // Get state for the given cameraId + auto state = getCameraState(cameraId); + if (state == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (no camera device with ID %s)", + clientPid, cameraId.string()); return BAD_VALUE; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return INVALID_OPERATION; } - } else { - // A particular HAL version is requested by caller. Create CameraClient - // based on the requested HAL version. - if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 && - halVersion == CAMERA_DEVICE_API_VERSION_1_0) { - // Only support higher HAL version device opened as HAL1.0 device. - client = new CameraClient(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid(), legacyMode); - } else { - // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet. - ALOGE("Invalid camera HAL version %x: HAL %x device can only be" - " opened as HAL %x device", halVersion, deviceVersion, - CAMERA_DEVICE_API_VERSION_1_0); - return INVALID_OPERATION; + + // Make descriptor for incoming client + clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId, + sp{nullptr}, static_cast(state->getCost()), + state->getConflicting(), + getCameraPriorityFromProcState(priorities[priorities.size() - 1]), clientPid); + + // Find clients that would be evicted + auto evicted = mActiveClientManager.wouldEvict(clientDescriptor); + + // If the incoming client was 'evicted,' higher priority clients have the camera in the + // background, so we cannot do evictions + if (std::find(evicted.begin(), evicted.end(), clientDescriptor) != evicted.end()) { + ALOGE("CameraService::connect X (PID %d) rejected (existing client(s) with higher" + " priority).", clientPid); + + sp clientSp = clientDescriptor->getValue(); + String8 curTime = getFormattedCurrentTime(); + auto incompatibleClients = + mActiveClientManager.getIncompatibleClients(clientDescriptor); + + String8 msg = String8::format("%s : DENIED connect device %s client for package %s " + "(PID %d, priority %d)", curTime.string(), + cameraId.string(), packageName.string(), clientPid, + getCameraPriorityFromProcState(priorities[priorities.size() - 1])); + + for (auto& i : incompatibleClients) { + msg.appendFormat("\n - Blocked by existing device %s client for package %s" + "(PID %" PRId32 ", priority %" PRId32 ")", i->getKey().string(), + String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(), + i->getPriority()); + } + + // Log the client's attempt + mEventLog.add(msg); + + return -EBUSY; + } + + for (auto& i : evicted) { + sp clientSp = i->getValue(); + if (clientSp.get() == nullptr) { + ALOGE("%s: Invalid state: Null client in active client list.", __FUNCTION__); + + // TODO: Remove this + LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, null client in active list", + __FUNCTION__); + mActiveClientManager.remove(i); + continue; + } + + ALOGE("CameraService::connect evicting conflicting client for camera ID %s", + i->getKey().string()); + evictedClients.push_back(clientSp); + + String8 curTime = getFormattedCurrentTime(); + + // Log the clients evicted + mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %" + PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for " + "package %s (PID %d, priority %" PRId32 ")", curTime.string(), + i->getKey().string(), String8{clientSp->getPackageName()}.string(), + i->getOwnerId(), i->getPriority(), cameraId.string(), + packageName.string(), clientPid, + getCameraPriorityFromProcState(priorities[priorities.size() - 1]))); + + // Notify the client of disconnection + clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, + CaptureResultExtras()); } } - status_t status = connectFinishUnsafe(client, client->getRemote()); - if (status != OK) { - // this is probably not recoverable.. maybe the client can try again - return status; + // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking + // other clients from connecting in mServiceLockWrapper if held + mServiceLock.unlock(); + + // Clear caller identity temporarily so client disconnect PID checks work correctly + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + + // Destroy evicted clients + for (auto& i : evictedClients) { + // Disconnect is blocking, and should only have returned when HAL has cleaned up + i->disconnect(); // Clients will remove themselves from the active client list here } + evictedClients.clear(); - mClient[cameraId] = client; - LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, - getpid()); + IPCThreadState::self()->restoreCallingIdentity(token); - return OK; + // Once clients have been disconnected, relock + mServiceLock.lock(); + + // Check again if the device was unplugged or something while we weren't holding mServiceLock + if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) { + return ret; + } + + *partial = clientDescriptor; + return NO_ERROR; } status_t CameraService::connect( @@ -822,47 +1012,18 @@ status_t CameraService::connect( /*out*/ sp& device) { - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); - - LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; - } - - - sp client; - { - Mutex::Autolock lock(mServiceLock); - sp clientTmp; - if (!canConnectUnsafe(cameraId, clientPackageName, - IInterface::asBinder(cameraClient), - /*out*/clientTmp)) { - return -EBUSY; - } else if (client.get() != NULL) { - device = static_cast(clientTmp.get()); - return OK; - } - - status = connectHelperLocked(/*out*/client, - cameraClient, - cameraId, - clientPackageName, - clientUid, - callingPid); - if (status != OK) { - return status; - } + status_t ret = NO_ERROR; + String8 id = String8::format("%d", cameraId); + sp client = nullptr; + ret = connectHelper(cameraClient, id, CAMERA_HAL_API_VERSION_UNSPECIFIED, + clientPackageName, clientUid, API_1, false, false, /*out*/client); + if(ret != NO_ERROR) { + return ret; } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) device = client; - return OK; + return NO_ERROR; } status_t CameraService::connectLegacy( @@ -887,70 +1048,51 @@ status_t CameraService::connectLegacy( return INVALID_OPERATION; } - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); - - LOG1("CameraService::connect legacy E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; - } - - sp client; - { - Mutex::Autolock lock(mServiceLock); - sp clientTmp; - if (!canConnectUnsafe(cameraId, clientPackageName, - IInterface::asBinder(cameraClient), - /*out*/clientTmp)) { - return -EBUSY; - } else if (client.get() != NULL) { - device = static_cast(clientTmp.get()); - return OK; - } - - status = connectHelperLocked(/*out*/client, - cameraClient, - cameraId, - clientPackageName, - clientUid, - callingPid, - halVersion, - /*legacyMode*/true); - if (status != OK) { - return status; - } + status_t ret = NO_ERROR; + String8 id = String8::format("%d", cameraId); + sp client = nullptr; + ret = connectHelper(cameraClient, id, halVersion, clientPackageName, + clientUid, API_1, true, false, /*out*/client); + if(ret != NO_ERROR) { + return ret; } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) device = client; - return OK; + return NO_ERROR; } -bool CameraService::validCameraIdForSetTorchMode(const String8& cameraId) { - // invalid string for int - if (cameraId.string() == NULL) { - return false; - } - errno = 0; - char *endptr; - long id = strtol(cameraId.string(), &endptr, 10); // base 10 - if (errno || id > INT_MAX || id < INT_MIN || *endptr != 0) { - return false; - } +status_t CameraService::connectPro(const sp& cameraCb, + int cameraId, + const String16& clientPackageName, + int clientUid, + /*out*/ + sp& device) { + ALOGE("%s: Unimplemented, please use connectDevice", __FUNCTION__); + return INVALID_OPERATION; +} - // id matches one of the plugged-in devices? - ICameraServiceListener::Status deviceStatus = getStatus(id); - if (deviceStatus != ICameraServiceListener::STATUS_PRESENT && - deviceStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) { - return false; +status_t CameraService::connectDevice( + const sp& cameraCb, + int cameraId, + const String16& clientPackageName, + int clientUid, + /*out*/ + sp& device) { + + status_t ret = NO_ERROR; + String8 id = String8::format("%d", cameraId); + sp client = nullptr; + ret = connectHelper(cameraCb, id, + CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, API_2, false, false, + /*out*/client); + + if(ret != NO_ERROR) { + return ret; } - return true; + device = client; + return NO_ERROR; } status_t CameraService::setTorchMode(const String16& cameraId, bool enabled, @@ -963,7 +1105,15 @@ status_t CameraService::setTorchMode(const String16& cameraId, bool enabled, String8 id = String8(cameraId.string()); // verify id is valid. - if (validCameraIdForSetTorchMode(id) == false) { + auto state = getCameraState(id); + if (state == nullptr) { + ALOGE("%s: camera id is invalid %s", id.string()); + return -EINVAL; + } + + ICameraServiceListener::Status cameraStatus = state->getStatus(); + if (cameraStatus != ICameraServiceListener::STATUS_PRESENT && + cameraStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) { ALOGE("%s: camera id is invalid %s", id.string()); return -EINVAL; } @@ -979,8 +1129,7 @@ status_t CameraService::setTorchMode(const String16& cameraId, bool enabled, } if (status == ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE) { - if (getStatus(atoi(id.string())) == - ICameraServiceListener::STATUS_NOT_AVAILABLE) { + if (cameraStatus == ICameraServiceListener::STATUS_NOT_AVAILABLE) { ALOGE("%s: torch mode of camera %s is not available because " "camera is in use", __FUNCTION__, id.string()); return -EBUSY; @@ -1022,174 +1171,6 @@ status_t CameraService::setTorchMode(const String16& cameraId, bool enabled, return OK; } -status_t CameraService::connectFinishUnsafe(const sp& client, - const sp& remoteCallback) { - status_t status = client->initialize(mModule); - if (status != OK) { - ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__); - return status; - } - if (remoteCallback != NULL) { - remoteCallback->linkToDeath(this); - } - - return OK; -} - -status_t CameraService::connectPro( - const sp& cameraCb, - int cameraId, - const String16& clientPackageName, - int clientUid, - /*out*/ - sp& device) -{ - if (cameraCb == 0) { - ALOGE("%s: Callback must not be null", __FUNCTION__); - return BAD_VALUE; - } - - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); - - LOG1("CameraService::connectPro E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; - } - - sp client; - { - Mutex::Autolock lock(mServiceLock); - { - sp client; - if (!canConnectUnsafe(cameraId, clientPackageName, - IInterface::asBinder(cameraCb), - /*out*/client)) { - return -EBUSY; - } - } - - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); - - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - ALOGE("Camera id %d uses HALv1, doesn't support ProCamera", - cameraId); - return -EOPNOTSUPP; - break; - case CAMERA_DEVICE_API_VERSION_2_0: - case CAMERA_DEVICE_API_VERSION_2_1: - case CAMERA_DEVICE_API_VERSION_3_0: - case CAMERA_DEVICE_API_VERSION_3_1: - case CAMERA_DEVICE_API_VERSION_3_2: - client = new ProCamera2Client(this, cameraCb, clientPackageName, - cameraId, facing, callingPid, clientUid, getpid()); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); - return BAD_VALUE; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return INVALID_OPERATION; - } - - status_t status = connectFinishUnsafe(client, client->getRemote()); - if (status != OK) { - return status; - } - - mProClientList[cameraId].push(client); - - LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId, - getpid()); - } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) - device = client; - return OK; -} - -status_t CameraService::connectDevice( - const sp& cameraCb, - int cameraId, - const String16& clientPackageName, - int clientUid, - /*out*/ - sp& device) -{ - - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); - - LOG1("CameraService::connectDevice E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; - } - - sp client; - { - Mutex::Autolock lock(mServiceLock); - { - sp client; - if (!canConnectUnsafe(cameraId, clientPackageName, - IInterface::asBinder(cameraCb), - /*out*/client)) { - return -EBUSY; - } - } - - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); - - // give flashlight a chance to close devices if necessary. - mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId)); - - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - ALOGW("Camera using old HAL version: %d", deviceVersion); - return -EOPNOTSUPP; - // TODO: don't allow 2.0 Only allow 2.1 and higher - case CAMERA_DEVICE_API_VERSION_2_0: - case CAMERA_DEVICE_API_VERSION_2_1: - case CAMERA_DEVICE_API_VERSION_3_0: - case CAMERA_DEVICE_API_VERSION_3_1: - case CAMERA_DEVICE_API_VERSION_3_2: - client = new CameraDeviceClient(this, cameraCb, clientPackageName, - cameraId, facing, callingPid, clientUid, getpid()); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); - return BAD_VALUE; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return INVALID_OPERATION; - } - - status_t status = connectFinishUnsafe(client, client->getRemote()); - if (status != OK) { - // this is probably not recoverable.. maybe the client can try again - return status; - } - - LOG1("CameraService::connectDevice X (id %d, this pid is %d)", cameraId, - getpid()); - - mClient[cameraId] = client; - } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) - - device = client; - return OK; -} - - status_t CameraService::addListener( const sp& listener) { ALOGV("%s: Add listener %p", __FUNCTION__, listener.get()); @@ -1201,23 +1182,29 @@ status_t CameraService::addListener( Mutex::Autolock lock(mServiceLock); - Vector >::iterator it, end; - for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { - if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { - ALOGW("%s: Tried to add listener %p which was already subscribed", - __FUNCTION__, listener.get()); - return ALREADY_EXISTS; + { + Mutex::Autolock lock(mStatusListenerLock); + for (auto& it : mListenerList) { + if (IInterface::asBinder(it) == IInterface::asBinder(listener)) { + ALOGW("%s: Tried to add listener %p which was already subscribed", + __FUNCTION__, listener.get()); + return ALREADY_EXISTS; + } } + + mListenerList.push_back(listener); } - mListenerList.push_back(listener); /* Immediately signal current status to this listener only */ { - Mutex::Autolock m(mStatusMutex) ; - int numCams = getNumberOfCameras(); - for (int i = 0; i < numCams; ++i) { - listener->onStatusChanged(mStatusList[i], i); + Mutex::Autolock lock(mCameraStatesLock); + for (auto& i : mCameraStates) { + // TODO: Update binder to use String16 for camera IDs and remove; + int id = cameraIdToInt(i.first); + if (id == -1) continue; + + listener->onStatusChanged(i.second->getStatus(), id); } } @@ -1228,13 +1215,12 @@ status_t CameraService::addListener( String16 id = String16(mTorchStatusMap.keyAt(i).string()); listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i), id); } - } return OK; } -status_t CameraService::removeListener( - const sp& listener) { + +status_t CameraService::removeListener(const sp& listener) { ALOGV("%s: Remove listener %p", __FUNCTION__, listener.get()); if (listener == 0) { @@ -1244,11 +1230,13 @@ status_t CameraService::removeListener( Mutex::Autolock lock(mServiceLock); - Vector >::iterator it; - for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { - if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { - mListenerList.erase(it); - return OK; + { + Mutex::Autolock lock(mStatusListenerLock); + for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) { + if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { + mListenerList.erase(it); + return OK; + } } } @@ -1258,10 +1246,7 @@ status_t CameraService::removeListener( return BAD_VALUE; } -status_t CameraService::getLegacyParameters( - int cameraId, - /*out*/ - String16* parameters) { +status_t CameraService::getLegacyParameters(int cameraId, /*out*/String16* parameters) { ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId); if (parameters == NULL) { @@ -1316,6 +1301,7 @@ status_t CameraService::supportsCameraApi(int cameraId, int apiVersion) { return OK; } case CAMERA_DEVICE_API_VERSION_3_2: + case CAMERA_DEVICE_API_VERSION_3_3: ALOGV("%s: Camera id %d uses HAL3.2 or newer, supports api1/api2 directly", __FUNCTION__, cameraId); return OK; @@ -1330,127 +1316,107 @@ status_t CameraService::supportsCameraApi(int cameraId, int apiVersion) { return OK; } -void CameraService::removeClientByRemote(const wp& remoteBinder) { - int callingPid = getCallingPid(); - LOG1("CameraService::removeClientByRemote E (pid %d)", callingPid); - - // Declare this before the lock to make absolutely sure the - // destructor won't be called with the lock held. +void CameraService::removeByClient(const BasicClient* client) { Mutex::Autolock lock(mServiceLock); - - int outIndex; - sp client = findClientUnsafe(remoteBinder, outIndex); - - if (client != 0) { - // Found our camera, clear and leave. - LOG1("removeClient: clear camera %d", outIndex); - - sp remote = client->getRemote(); - if (remote != NULL) { - remote->unlinkToDeath(this); - } - - mClient[outIndex].clear(); - } else { - - sp clientPro = findProClientUnsafe(remoteBinder); - - if (clientPro != NULL) { - // Found our camera, clear and leave. - LOG1("removeClient: clear pro %p", clientPro.get()); - - IInterface::asBinder(clientPro->getRemoteCallback())->unlinkToDeath(this); + for (auto& i : mActiveClientManager.getAll()) { + auto clientSp = i->getValue(); + if (clientSp.get() == client) { + mActiveClientManager.remove(i); } } - - LOG1("CameraService::removeClientByRemote X (pid %d)", callingPid); } -sp CameraService::findProClientUnsafe( - const wp& cameraCallbacksRemote) -{ - sp clientPro; - - for (int i = 0; i < mNumberOfCameras; ++i) { - Vector removeIdx; +bool CameraService::evictClientIdByRemote(const wp& remote) { + const int callingPid = getCallingPid(); + const int servicePid = getpid(); + bool ret = false; + { + // Acquire mServiceLock and prevent other clients from connecting + std::unique_ptr lock = + AutoConditionLock::waitAndAcquire(mServiceLockWrapper); - for (size_t j = 0; j < mProClientList[i].size(); ++j) { - wp cl = mProClientList[i][j]; - sp clStrong = cl.promote(); - if (clStrong != NULL && clStrong->getRemote() == cameraCallbacksRemote) { - clientPro = clStrong; - break; - } else if (clStrong == NULL) { - // mark to clean up dead ptr - removeIdx.push(j); + std::vector> evicted; + for (auto& i : mActiveClientManager.getAll()) { + auto clientSp = i->getValue(); + if (clientSp.get() == nullptr) { + ALOGE("%s: Dead client still in mActiveClientManager.", __FUNCTION__); + mActiveClientManager.remove(i); + continue; + } + if (remote == clientSp->getRemote() && (callingPid == servicePid || + callingPid == clientSp->getClientPid())) { + mActiveClientManager.remove(i); + evicted.push_back(clientSp); + + // Notify the client of disconnection + clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, + CaptureResultExtras()); } } - // remove stale ptrs (in reverse so the indices dont change) - for (ssize_t j = (ssize_t)removeIdx.size() - 1; j >= 0; --j) { - mProClientList[i].removeAt(removeIdx[j]); - } - - } - - return clientPro; -} + // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking + // other clients from connecting in mServiceLockWrapper if held + mServiceLock.unlock(); -sp CameraService::findClientUnsafe( - const wp& cameraClient, int& outIndex) { - sp client; + for (auto& i : evicted) { + if (i.get() != nullptr) { + i->disconnect(); + ret = true; + } + } - for (int i = 0; i < mNumberOfCameras; i++) { + // Reacquire mServiceLock + mServiceLock.lock(); - // This happens when we have already disconnected (or this is - // just another unused camera). - if (mClient[i] == 0) continue; + } // lock is destroyed, allow further connect calls - // Promote mClient. It can fail if we are called from this path: - // Client::~Client() -> disconnect() -> removeClientByRemote(). - client = mClient[i].promote(); + return ret; +} - // Clean up stale client entry - if (client == NULL) { - mClient[i].clear(); - continue; - } - if (cameraClient == client->getRemote()) { - // Found our camera - outIndex = i; - return client; +std::shared_ptr CameraService::getCameraState( + const String8& cameraId) const { + std::shared_ptr state; + { + Mutex::Autolock lock(mCameraStatesLock); + auto iter = mCameraStates.find(cameraId); + if (iter != mCameraStates.end()) { + state = iter->second; } } - - outIndex = -1; - return NULL; + return state; } -CameraService::BasicClient* CameraService::getClientByIdUnsafe(int cameraId) { - if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL; - return mClient[cameraId].unsafe_get(); -} +sp CameraService::removeClientLocked(const String8& cameraId) { + // Remove from active clients list + auto clientDescriptorPtr = mActiveClientManager.remove(cameraId); + if (clientDescriptorPtr == nullptr) { + ALOGW("%s: Could not evict client, no client for camera ID %s", __FUNCTION__, + cameraId.string()); + return sp{nullptr}; + } -Mutex* CameraService::getClientLockById(int cameraId) { - if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL; - return &mClientLock[cameraId]; + return clientDescriptorPtr->getValue(); } -sp CameraService::getClientByRemote( - const wp& cameraClient) { - // Declare this before the lock to make absolutely sure the - // destructor won't be called with the lock held. - sp client; +void CameraService::logDisconnected(const String8& cameraId, int clientPid, + const String8& clientPackage) { - Mutex::Autolock lock(mServiceLock); + String8 curTime = getFormattedCurrentTime(); + // Log the clients evicted + mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)", + curTime.string(), cameraId.string(), clientPackage.string(), clientPid)); +} - int outIndex; - client = findClientUnsafe(cameraClient, outIndex); +void CameraService::logConnected(const String8& cameraId, int clientPid, + const String8& clientPackage) { - return client; + String8 curTime = getFormattedCurrentTime(); + // Log the clients evicted + mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)", + curTime.string(), cameraId.string(), clientPackage.string(), clientPid)); } status_t CameraService::onTransact( @@ -1479,24 +1445,6 @@ status_t CameraService::onTransact( return BnCameraService::onTransact(code, data, reply, flags); } -// The reason we need this busy bit is a new CameraService::connect() request -// may come in while the previous Client's destructor has not been run or is -// still running. If the last strong reference of the previous Client is gone -// but the destructor has not been finished, we should not allow the new Client -// to be created because we need to wait for the previous Client to tear down -// the hardware first. -void CameraService::setCameraBusy(int cameraId) { - android_atomic_write(1, &mBusy[cameraId]); - - ALOGV("setCameraBusy cameraId=%d", cameraId); -} - -void CameraService::setCameraFree(int cameraId) { - android_atomic_write(0, &mBusy[cameraId]); - - ALOGV("setCameraFree cameraId=%d", cameraId); -} - // We share the media players for shutter and recording sound for all clients. // A reference count is kept to determine when we will actually release the // media players. @@ -1565,7 +1513,6 @@ CameraService::Client::Client(const sp& cameraService, mRemoteCallback = cameraClient; - cameraService->setCameraBusy(cameraId); cameraService->loadSound(); LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId); @@ -1587,7 +1534,7 @@ CameraService::BasicClient::BasicClient(const sp& cameraService, int cameraId, int cameraFacing, int clientPid, uid_t clientUid, int servicePid): - mClientPackageName(clientPackageName) + mClientPackageName(clientPackageName), mDisconnected(false) { mCameraService = cameraService; mRemoteBinder = remoteCallback; @@ -1606,14 +1553,34 @@ CameraService::BasicClient::~BasicClient() { } void CameraService::BasicClient::disconnect() { - ALOGV("BasicClient::disconnect"); - mCameraService->removeClientByRemote(mRemoteBinder); + if (mDisconnected) return; + mDisconnected = true;; + + mCameraService->removeByClient(this); + mCameraService->logDisconnected(String8::format("%d", mCameraId), mClientPid, + String8(mClientPackageName)); + + sp remote = getRemote(); + if (remote != nullptr) { + remote->unlinkToDeath(mCameraService); + } finishCameraOps(); + ALOGI("%s: Disconnected client for camera %d for PID %d", __FUNCTION__, mCameraId, mClientPid); + // client shouldn't be able to call into us anymore mClientPid = 0; } +String16 CameraService::BasicClient::getPackageName() const { + return mClientPackageName; +} + + +int CameraService::BasicClient::getClientPid() const { + return mClientPid; +} + status_t CameraService::BasicClient::startCameraOps() { int32_t res; // Notify app ops that the camera is not available @@ -1639,7 +1606,7 @@ status_t CameraService::BasicClient::startCameraOps() { // Transition device availability listeners from PRESENT -> NOT_AVAILABLE mCameraService->updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, - mCameraId); + String8::format("%d", mCameraId)); return OK; } @@ -1652,18 +1619,12 @@ status_t CameraService::BasicClient::finishCameraOps() { mClientPackageName); mOpsActive = false; - // Notify device availability listeners that this camera is available - // again - - StatusVector rejectSourceStates; - rejectSourceStates.push_back(ICameraServiceListener::STATUS_NOT_PRESENT); - rejectSourceStates.push_back(ICameraServiceListener::STATUS_ENUMERATING); + auto rejected = {ICameraServiceListener::STATUS_NOT_PRESENT, + ICameraServiceListener::STATUS_ENUMERATING}; - // Transition to PRESENT if the camera is not in either of above 2 - // states + // Transition to PRESENT if the camera is not in either of the rejected states mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT, - mCameraId, - &rejectSourceStates); + String8::format("%d", mCameraId), rejected); // Notify flashlight that a camera device is closed. mCameraService->mFlashlight->deviceClosed( @@ -1710,26 +1671,15 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16& packageNa // ---------------------------------------------------------------------------- -Mutex* CameraService::Client::getClientLockFromCookie(void* user) { - return gCameraService->getClientLockById((int)(intptr_t) user); -} - -// Provide client pointer for callbacks. Client lock returned from getClientLockFromCookie should -// be acquired for this to be safe -CameraService::Client* CameraService::Client::getClientFromCookie(void* user) { - BasicClient *basicClient = gCameraService->getClientByIdUnsafe((int)(intptr_t) user); - // OK: only CameraClient calls this, and they already cast anyway. - Client* client = static_cast(basicClient); - - // This could happen if the Client is in the process of shutting down (the - // last strong reference is gone, but the destructor hasn't finished - // stopping the hardware). - if (client == NULL) return NULL; - - // destruction already started, so should not be accessed - if (client->mDestructionStarted) return NULL; - - return client; +// Provide client strong pointer for callbacks. +sp CameraService::Client::getClientFromCookie(void* user) { + String8 cameraId = String8::format("%d", (int)(intptr_t) user); + auto clientDescriptor = gCameraService->mActiveClientManager.get(cameraId); + if (clientDescriptor != nullptr) { + return sp{ + static_cast(clientDescriptor->getValue().get())}; + } + return sp{nullptr}; } void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, @@ -1741,7 +1691,6 @@ void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode void CameraService::Client::disconnect() { ALOGV("Client::disconnect"); BasicClient::disconnect(); - mCameraService->setCameraFree(mCameraId); } CameraService::Client::OpsCallback::OpsCallback(wp client): @@ -1784,6 +1733,104 @@ void CameraService::ProClient::notifyError(ICameraDeviceCallbacks::CameraErrorCo } // ---------------------------------------------------------------------------- +// CameraState +// ---------------------------------------------------------------------------- + +CameraService::CameraState::CameraState(const String8& id, int cost, + const std::set& conflicting) : mId(id), + mStatus(ICameraServiceListener::STATUS_PRESENT), mCost(cost), mConflicting(conflicting) {} + +CameraService::CameraState::~CameraState() {} + +ICameraServiceListener::Status CameraService::CameraState::getStatus() const { + Mutex::Autolock lock(mStatusLock); + return mStatus; +} + +CameraParameters CameraService::CameraState::getShimParams() const { + return mShimParams; +} + +void CameraService::CameraState::setShimParams(const CameraParameters& params) { + mShimParams = params; +} + +int CameraService::CameraState::getCost() const { + return mCost; +} + +std::set CameraService::CameraState::getConflicting() const { + return mConflicting; +} + +String8 CameraService::CameraState::getId() const { + return mId; +} + +// ---------------------------------------------------------------------------- +// CameraClientManager +// ---------------------------------------------------------------------------- + +CameraService::CameraClientManager::~CameraClientManager() {} + +sp CameraService::CameraClientManager::getCameraClient( + const String8& id) const { + auto descriptor = get(id); + if (descriptor == nullptr) { + return sp{nullptr}; + } + return descriptor->getValue(); +} + +String8 CameraService::CameraClientManager::toString() const { + auto all = getAll(); + String8 ret("["); + bool hasAny = false; + for (auto& i : all) { + hasAny = true; + String8 key = i->getKey(); + int32_t cost = i->getCost(); + int32_t pid = i->getOwnerId(); + int32_t priority = i->getPriority(); + auto conflicting = i->getConflicting(); + auto clientSp = i->getValue(); + String8 packageName; + if (clientSp.get() != nullptr) { + packageName = String8{clientSp->getPackageName()}; + } + ret.appendFormat("\n(Camera ID: %s, Cost: %" PRId32 ", PID: %" PRId32 ", Priority: %" + PRId32 ", ", key.string(), cost, pid, priority); + + if (packageName.size() != 0) { + ret.appendFormat("Client Package Name: %s", packageName.string()); + } + + ret.append(", Conflicting Client Devices: {"); + for (auto& j : conflicting) { + ret.appendFormat("%s, ", j.string()); + } + ret.append("})"); + } + if (hasAny) ret.append("\n"); + ret.append("]\n"); + return ret; +} + +CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor( + const String8& key, const sp& value, int32_t cost, + const std::set& conflictingKeys, int32_t priority, int32_t ownerId) { + + return std::make_shared>>( + key, value, cost, conflictingKeys, priority, ownerId); +} + +CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor( + const sp& value, const CameraService::DescriptorPtr& partial) { + return makeClientDescriptor(partial->getKey(), value, partial->getCost(), + partial->getConflicting(), partial->getPriority(), partial->getOwnerId()); +} + +// ---------------------------------------------------------------------------- static const int kDumpLockRetries = 50; static const int kDumpLockSleep = 60000; @@ -1826,11 +1873,14 @@ status_t CameraService::dump(int fd, const Vector& args) { } const hw_module_t* common = mModule->getRawModule(); - result = String8::format("Camera module HAL API version: 0x%x\n", common->hal_api_version); - result.appendFormat("Camera module API version: 0x%x\n", common->module_api_version); + result = String8::format("Camera module HAL API version: %#x\n", common->hal_api_version); + result.appendFormat("Camera module API version: %#x\n", common->module_api_version); result.appendFormat("Camera module name: %s\n", common->name); result.appendFormat("Camera module author: %s\n", common->author); - result.appendFormat("Number of camera devices: %d\n\n", mNumberOfCameras); + result.appendFormat("Number of camera devices: %d\n", mNumberOfCameras); + String8 activeClientString = mActiveClientManager.toString(); + result.appendFormat("Active Camera Clients:\n%s", activeClientString.string()); + sp desc = VendorTagDescriptor::getGlobalVendorTagDescriptor(); if (desc == NULL) { @@ -1845,11 +1895,31 @@ status_t CameraService::dump(int fd, const Vector& args) { desc->dump(fd, /*verbosity*/2, /*indentation*/4); } - for (int i = 0; i < mNumberOfCameras; i++) { - result = String8::format("Camera %d static information:\n", i); + result = String8("Prior client events (most recent at top):\n"); + + for (const auto& msg : mEventLog) { + result.appendFormat("%s\n", msg.string()); + } + + if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) { + result.append("...\n"); + } + + write(fd, result.string(), result.size()); + + bool stateLocked = tryLock(mCameraStatesLock); + if (!stateLocked) { + result = String8::format("CameraStates in use, may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + for (auto& state : mCameraStates) { + String8 cameraId = state.first; + result = String8::format("Camera %s information:\n", cameraId.string()); camera_info info; - status_t rc = mModule->getCameraInfo(i, &info); + // TODO: Change getCameraInfo + HAL to use String cameraIds + status_t rc = mModule->getCameraInfo(cameraIdToInt(cameraId), &info); if (rc != OK) { result.appendFormat(" Error reading static information!\n"); write(fd, result.string(), result.size()); @@ -1863,7 +1933,19 @@ status_t CameraService::dump(int fd, const Vector& args) { } else { deviceVersion = info.device_version; } - result.appendFormat(" Device version: 0x%x\n", deviceVersion); + + auto conflicting = state.second->getConflicting(); + result.appendFormat(" Resource Cost: %d\n", state.second->getCost()); + result.appendFormat(" Conflicting Devices:"); + for (auto& id : conflicting) { + result.appendFormat(" %s", cameraId.string()); + } + if (conflicting.size() == 0) { + result.appendFormat(" NONE"); + } + result.appendFormat("\n"); + + result.appendFormat(" Device version: %#x\n", deviceVersion); if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) { result.appendFormat(" Device static metadata:\n"); write(fd, result.string(), result.size()); @@ -1872,19 +1954,38 @@ status_t CameraService::dump(int fd, const Vector& args) { } else { write(fd, result.string(), result.size()); } + + CameraParameters p = state.second->getShimParams(); + if (!p.isEmpty()) { + result = String8::format(" Camera1 API shim is using parameters:\n "); + write(fd, result.string(), result.size()); + p.dump(fd, args); + } } - sp client = mClient[i].promote(); - if (client == 0) { - result = String8::format(" Device is closed, no client instance\n"); + auto clientDescriptor = mActiveClientManager.get(cameraId); + if (clientDescriptor == nullptr) { + result = String8::format(" Device %s is closed, no client instance\n", + cameraId.string()); write(fd, result.string(), result.size()); continue; } hasClient = true; - result = String8::format(" Device is open. Client instance dump:\n"); + result = String8::format(" Device %s is open. Client instance dump:\n\n", + cameraId.string()); + result.appendFormat("Client priority level: %d\n", clientDescriptor->getPriority()); + result.appendFormat("Client PID: %d\n", clientDescriptor->getOwnerId()); + + auto client = clientDescriptor->getValue(); + result.appendFormat("Client package: %s\n", + String8(client->getPackageName()).string()); write(fd, result.string(), result.size()); + client->dump(fd, args); } + + if (stateLocked) mCameraStatesLock.unlock(); + if (!hasClient) { result = String8::format("\nNo active camera clients yet.\n"); write(fd, result.string(), result.size()); @@ -1908,7 +2009,6 @@ status_t CameraService::dump(int fd, const Vector& args) { write(fd, result.string(), result.size()); } } - } return NO_ERROR; } @@ -1931,124 +2031,68 @@ void CameraService::handleTorchClientBinderDied(const wp &who) { } } -/*virtual*/void CameraService::binderDied( - const wp &who) { +/*virtual*/void CameraService::binderDied(const wp &who) { /** * While tempting to promote the wp into a sp, * it's actually not supported by the binder driver */ - ALOGV("java clients' binder died"); - // check torch client handleTorchClientBinderDied(who); // check camera device client - sp cameraClient = getClientByRemote(who); - - if (cameraClient == 0) { - ALOGV("java clients' binder death already cleaned up (normal case)"); + if(!evictClientIdByRemote(who)) { + ALOGV("%s: Java client's binder death already cleaned up (normal case)", __FUNCTION__); return; } - ALOGW("Disconnecting camera client %p since the binder for it " - "died (this pid %d)", cameraClient.get(), getCallingPid()); - - cameraClient->disconnect(); - + ALOGE("%s: Java client's binder died, removing it from the list of active clients", + __FUNCTION__); } -void CameraService::updateStatus(ICameraServiceListener::Status status, - int32_t cameraId, - const StatusVector *rejectSourceStates) { - // do not lock mServiceLock here or can get into a deadlock from - // connect() -> ProClient::disconnect -> updateStatus - Mutex::Autolock lock(mStatusMutex); - - ICameraServiceListener::Status oldStatus = mStatusList[cameraId]; - - mStatusList[cameraId] = status; - - if (oldStatus != status) { - ALOGV("%s: Status has changed for camera ID %d from 0x%x to 0x%x", - __FUNCTION__, cameraId, (uint32_t)oldStatus, (uint32_t)status); +void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId) { + updateStatus(status, cameraId, {}); +} - if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT && - (status != ICameraServiceListener::STATUS_PRESENT && - status != ICameraServiceListener::STATUS_ENUMERATING)) { +void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId, + std::initializer_list rejectSourceStates) { + // Do not lock mServiceLock here or can get into a deadlock from + // connect() -> disconnect -> updateStatus - ALOGW("%s: From NOT_PRESENT can only transition into PRESENT" - " or ENUMERATING", __FUNCTION__); - mStatusList[cameraId] = oldStatus; - return; - } + auto state = getCameraState(cameraId); - if (rejectSourceStates != NULL) { - const StatusVector &rejectList = *rejectSourceStates; - StatusVector::const_iterator it = rejectList.begin(); - - /** - * Sometimes we want to conditionally do a transition. - * For example if a client disconnects, we want to go to PRESENT - * only if we weren't already in NOT_PRESENT or ENUMERATING. - */ - for (; it != rejectList.end(); ++it) { - if (oldStatus == *it) { - ALOGV("%s: Rejecting status transition for Camera ID %d, " - " since the source state was was in one of the bad " - " states.", __FUNCTION__, cameraId); - mStatusList[cameraId] = oldStatus; - return; - } - } - } + if (state == nullptr) { + ALOGW("%s: Could not update the status for %s, no such device exists", __FUNCTION__, + cameraId.string()); + return; + } - /** - * ProClients lose their exclusive lock. - * - Done before the CameraClient can initialize the HAL device, - * since we want to be able to close it before they get to initialize - */ - if (status == ICameraServiceListener::STATUS_NOT_AVAILABLE) { - Vector > proClients(mProClientList[cameraId]); - Vector >::const_iterator it; - - for (it = proClients.begin(); it != proClients.end(); ++it) { - sp proCl = it->promote(); - if (proCl.get() != NULL) { - proCl->onExclusiveLockStolen(); - } + // Update the status for this camera state, then send the onStatusChangedCallbacks to each + // of the listeners with both the mStatusStatus and mStatusListenerLock held + state->updateStatus(status, cameraId, rejectSourceStates, [this] + (const String8& cameraId, ICameraServiceListener::Status status) { + + // Update torch status + if (status == ICameraServiceListener::STATUS_NOT_PRESENT || + status == ICameraServiceListener::STATUS_NOT_AVAILABLE) { + // Update torch status to not available when the camera device becomes not present + // or not available. + onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE); + } else if (status == ICameraServiceListener::STATUS_PRESENT) { + // Update torch status to available when the camera device becomes present or + // available + onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF); } - } - if (status == ICameraServiceListener::STATUS_NOT_PRESENT || - status == ICameraServiceListener::STATUS_NOT_AVAILABLE) { - // update torch status to not available when the camera device - // becomes not present or not available. - onTorchStatusChanged(String8::format("%d", cameraId), - ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE); - } else if (status == ICameraServiceListener::STATUS_PRESENT) { - // update torch status to available when the camera device becomes - // present or available - onTorchStatusChanged(String8::format("%d", cameraId), - ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF); - } + Mutex::Autolock lock(mStatusListenerLock); - Vector >::const_iterator it; - for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { - (*it)->onStatusChanged(status, cameraId); - } - } -} - -ICameraServiceListener::Status CameraService::getStatus(int cameraId) const { - if (cameraId < 0 || cameraId >= MAX_CAMERAS) { - ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId); - return ICameraServiceListener::STATUS_UNKNOWN; - } - - Mutex::Autolock al(mStatusMutex); - return mStatusList[cameraId]; + for (auto& listener : mListenerList) { + // TODO: Refactor status listeners to use strings for Camera IDs and remove this. + int id = cameraIdToInt(cameraId); + if (id != -1) listener->onStatusChanged(status, id); + } + }); } status_t CameraService::getTorchStatusLocked( diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 22afc8c..53420e5 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -38,11 +38,15 @@ #include #include "CameraFlashlight.h" - #include "common/CameraModule.h" +#include "utils/AutoConditionLock.h" +#include "utils/ClientManager.h" +#include "utils/RingBuffer.h" -/* This needs to be increased if we can have more cameras */ -#define MAX_CAMERAS 2 +#include +#include +#include +#include namespace android { @@ -62,6 +66,34 @@ public: class Client; class BasicClient; + enum apiLevel { + API_1 = 1, + API_2 = 2 + }; + + // Process States (mirrors frameworks/base/core/java/android/app/ActivityManager.java) + static const int PROCESS_STATE_NONEXISTENT = -1; + static const int PROCESS_STATE_PERSISTENT = 0; + static const int PROCESS_STATE_PERSISTENT_UI = 1; + static const int PROCESS_STATE_TOP = 2; + static const int PROCESS_STATE_IMPORTANT_FOREGROUND = 3; + static const int PROCESS_STATE_IMPORTANT_BACKGROUND = 4; + static const int PROCESS_STATE_BACKUP = 5; + static const int PROCESS_STATE_HEAVY_WEIGHT = 6; + static const int PROCESS_STATE_SERVICE = 7; + static const int PROCESS_STATE_RECEIVER = 8; + static const int PROCESS_STATE_HOME = 9; + static const int PROCESS_STATE_LAST_ACTIVITY = 10; + static const int PROCESS_STATE_CACHED_ACTIVITY = 11; + static const int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12; + static const int PROCESS_STATE_CACHED_EMPTY = 13; + + // 3 second busy timeout when other clients are connecting + static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000; + + // Default number of messages to store in eviction log + static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50; + // Implementation of BinderService static char const* getServiceName() { return "media.camera"; } @@ -70,8 +102,8 @@ public: ///////////////////////////////////////////////////////////////////// // HAL Callbacks - virtual void onDeviceStatusChanged(int cameraId, - int newStatus); + virtual void onDeviceStatusChanged(camera_device_status_t cameraId, + camera_device_status_t newStatus); virtual void onTorchStatusChanged(const String8& cameraId, ICameraServiceListener::TorchStatus newStatus); @@ -132,7 +164,6 @@ public: ///////////////////////////////////////////////////////////////////// // Client functionality - virtual void removeClientByRemote(const wp& remoteBinder); enum sound_kind { SOUND_SHUTTER = 0, @@ -155,11 +186,6 @@ public: ///////////////////////////////////////////////////////////////////// // CameraClient functionality - // returns plain pointer of client. Note that mClientLock should be acquired to - // prevent the client from destruction. The result can be NULL. - virtual BasicClient* getClientByIdUnsafe(int cameraId); - virtual Mutex* getClientLockById(int cameraId); - class BasicClient : public virtual RefBase { public: virtual status_t initialize(CameraModule *module) = 0; @@ -176,6 +202,15 @@ public: virtual status_t dump(int fd, const Vector& args) = 0; + // Return the package name for this client + virtual String16 getPackageName() const; + + // Notify client about a fatal error + virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras) = 0; + + // Get the PID of the application client using this + virtual int getClientPid() const; protected: BasicClient(const sp& cameraService, const sp& remoteCallback, @@ -202,6 +237,7 @@ public: pid_t mClientPid; uid_t mClientUid; // immutable after constructor pid_t mServicePid; // immutable after constructor + bool mDisconnected; // - The app-side Binder interface to receive callbacks from us sp mRemoteBinder; // immutable after constructor @@ -210,10 +246,6 @@ public: status_t startCameraOps(); status_t finishCameraOps(); - // Notify client about a fatal error - virtual void notifyError( - ICameraDeviceCallbacks::CameraErrorCode errorCode, - const CaptureResultExtras& resultExtras) = 0; private: AppOpsManager mAppOpsManager; @@ -285,13 +317,11 @@ public: return asBinder(this); } - protected: - static Mutex* getClientLockFromCookie(void* user); - // convert client from cookie. Client lock should be acquired before getting Client. - static Client* getClientFromCookie(void* user); - virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, const CaptureResultExtras& resultExtras); + protected: + // Convert client from cookie. + static sp getClientFromCookie(void* user); // Initialized in constructor @@ -338,54 +368,230 @@ public: // Callbacks from camera service virtual void onExclusiveLockStolen() = 0; - protected: virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, const CaptureResultExtras& resultExtras); + protected: sp mRemoteCallback; }; // class ProClient + typedef std::shared_ptr>> DescriptorPtr; + + /** + * A container class for managing active camera clients that are using HAL devices. Active + * clients are represented by ClientDescriptor objects that contain strong pointers to the + * actual BasicClient subclass binder interface implementation. + * + * This class manages the eviction behavior for the camera clients. See the parent class + * implementation in utils/ClientManager for the specifics of this behavior. + */ + class CameraClientManager : + public resource_policy::ClientManager> { + public: + virtual ~CameraClientManager(); + + /** + * Return a strong pointer to the active BasicClient for this camera ID, or an empty + * if none exists. + */ + sp getCameraClient(const String8& id) const; + + /** + * Return a string describing the current state. + */ + String8 toString() const; + + /** + * Make a ClientDescriptor object wrapping the given BasicClient strong pointer. + */ + static DescriptorPtr makeClientDescriptor(const String8& key, const sp& value, + int32_t cost, const std::set& conflictingKeys, int32_t priority, + int32_t ownerId); + + /** + * Make a ClientDescriptor object wrapping the given BasicClient strong pointer with + * values intialized from a prior ClientDescriptor. + */ + static DescriptorPtr makeClientDescriptor(const sp& value, + const CameraService::DescriptorPtr& partial); + + }; // class CameraClientManager + private: + /** + * Container class for the state of each logical camera device, including: ID, status, and + * dependencies on other devices. The mapping of camera ID -> state saved in mCameraStates + * represents the camera devices advertised by the HAL (and any USB devices, when we add + * those). + * + * This container does NOT represent an active camera client. These are represented using + * the ClientDescriptors stored in mActiveClientManager. + */ + class CameraState { + public: + /** + * Make a new CameraState and set the ID, cost, and conflicting devices using the values + * returned in the HAL's camera_info struct for each device. + */ + CameraState(const String8& id, int cost, const std::set& conflicting); + virtual ~CameraState(); + + /** + * Return the status for this device. + * + * This method acquires mStatusLock. + */ + ICameraServiceListener::Status getStatus() const; + + /** + * This function updates the status for this camera device, unless the given status + * is in the given list of rejected status states, and execute the function passed in + * with a signature onStatusUpdateLocked(const String8&, ICameraServiceListener::Status) + * if the status has changed. + * + * This method is idempotent, and will not result in the function passed to + * onStatusUpdateLocked being called more than once for the same arguments. + * This method aquires mStatusLock. + */ + template + void updateStatus(ICameraServiceListener::Status status, const String8& cameraId, + std::initializer_list rejectSourceStates, + Func onStatusUpdatedLocked); + + /** + * Return the last set CameraParameters object generated from the information returned by + * the HAL for this device (or an empty CameraParameters object if none has been set). + */ + CameraParameters getShimParams() const; + + /** + * Set the CameraParameters for this device. + */ + void setShimParams(const CameraParameters& params); + + /** + * Return the resource_cost advertised by the HAL for this device. + */ + int getCost() const; + + /** + * Return a set of the IDs of conflicting devices advertised by the HAL for this device. + */ + std::set getConflicting() const; + + /** + * Return the ID of this camera device. + */ + String8 getId() const; + + private: + const String8 mId; + ICameraServiceListener::Status mStatus; // protected by mStatusLock + const int mCost; + std::set mConflicting; + mutable Mutex mStatusLock; + CameraParameters mShimParams; + }; // class CameraState + // Delay-load the Camera HAL module virtual void onFirstRef(); - // Step 1. Check if we can connect, before we acquire the service lock. - status_t validateConnect(int cameraId, - /*inout*/ - int& clientUid) const; + // Check if we can connect, before we acquire the service lock. + status_t validateConnect(const String8& cameraId, /*inout*/int& clientUid) const; - // Step 2. Check if we can connect, after we acquire the service lock. - bool canConnectUnsafe(int cameraId, - const String16& clientPackageName, - const sp& remoteCallback, - /*out*/ - sp &client); + // Handle active client evictions, and update service state. + // Only call with with mServiceLock held. + status_t handleEvictionsLocked(const String8& cameraId, int clientPid, + apiLevel effectiveApiLevel, const sp& remoteCallback, const String8& packageName, + /*out*/ + sp* client, + std::shared_ptr>>* partial); - // When connection is successful, initialize client and track its death - status_t connectFinishUnsafe(const sp& client, - const sp& remoteCallback); + // Single implementation shared between the various connect calls + template + status_t connectHelper(const sp& cameraCb, const String8& cameraId, int halVersion, + const String16& clientPackageName, int clientUid, apiLevel effectiveApiLevel, + bool legacyMode, bool shimUpdateOnly, /*out*/sp& device); - virtual sp getClientByRemote(const wp& cameraClient); + // Lock guarding camera service state Mutex mServiceLock; - // either a Client or CameraDeviceClient - wp mClient[MAX_CAMERAS]; // protected by mServiceLock - Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks - int mNumberOfCameras; - typedef wp weak_pro_client_ptr; - Vector mProClientList[MAX_CAMERAS]; + // Condition to use with mServiceLock, used to handle simultaneous connect calls from clients + std::shared_ptr mServiceLockWrapper; + + // Return NO_ERROR if the device with a give ID can be connected to + status_t checkIfDeviceIsUsable(const String8& cameraId) const; + + // Container for managing currently active application-layer clients + CameraClientManager mActiveClientManager; - // needs to be called with mServiceLock held - sp findClientUnsafe(const wp& cameraClient, int& outIndex); - sp findProClientUnsafe( - const wp& cameraCallbacksRemote); + // Mapping from camera ID -> state for each device, map is protected by mCameraStatesLock + std::map> mCameraStates; - // atomics to record whether the hardware is allocated to some client. - volatile int32_t mBusy[MAX_CAMERAS]; - void setCameraBusy(int cameraId); - void setCameraFree(int cameraId); + // Mutex guarding mCameraStates map + mutable Mutex mCameraStatesLock; + + // Circular buffer for storing event logging for dumps + RingBuffer mEventLog; + + /** + * Get the camera state for a given camera id. + * + * This acquires mCameraStatesLock. + */ + std::shared_ptr getCameraState(const String8& cameraId) const; + + /** + * Evict client who's remote binder has died. Returns true if this client was in the active + * list and was disconnected. + * + * This method acquires mServiceLock. + */ + bool evictClientIdByRemote(const wp& cameraClient); + + /** + * Remove the given client from the active clients list; does not disconnect the client. + * + * This method acquires mServiceLock. + */ + void removeByClient(const BasicClient* client); + + /** + * Add new client to active clients list after conflicting clients have disconnected using the + * values set in the partial descriptor passed in to construct the actual client descriptor. + * This is typically called at the end of a connect call. + * + * This method must be called with mServiceLock held. + */ + void finishConnectLocked(const sp& client, const DescriptorPtr& desc); + + /** + * Returns the integer corresponding to the given camera ID string, or -1 on failure. + */ + static int cameraIdToInt(const String8& cameraId); + + /** + * Remove a single client corresponding to the given camera id from the list of active clients. + * If none exists, return an empty strongpointer. + * + * This method must be called with mServiceLock held. + */ + sp removeClientLocked(const String8& cameraId); + + /** + * Add a event log message that a client has been disconnected. + */ + void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage); + + /** + * Add a event log message that a client has been connected. + */ + void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage); + + int mNumberOfCameras; // sounds MediaPlayer* newMediaPlayer(const char *file); @@ -396,24 +602,21 @@ private: CameraModule* mModule; - Vector > - mListenerList; - - // guard only mStatusList and the broadcasting of ICameraServiceListener - mutable Mutex mStatusMutex; - ICameraServiceListener::Status - mStatusList[MAX_CAMERAS]; + // Guarded by mStatusListenerMutex + std::vector> mListenerList; + Mutex mStatusListenerLock; - // Read the current status (locks mStatusMutex) - ICameraServiceListener::Status - getStatus(int cameraId) const; - - typedef Vector StatusVector; - // Broadcast the new status if it changed (locks the service mutex) - void updateStatus( - ICameraServiceListener::Status status, - int32_t cameraId, - const StatusVector *rejectSourceStates = NULL); + /** + * Update the status for the given camera id (if that device exists), and broadcast the + * status update to all current ICameraServiceListeners if the status has changed. Any + * statuses in rejectedSourceStates will be ignored. + * + * This method must be idempotent. + * This method acquires mStatusLock and mStatusListenerLock. + */ + void updateStatus(ICameraServiceListener::Status status, const String8& cameraId, + std::initializer_list rejectedSourceStates); + void updateStatus(ICameraServiceListener::Status status, const String8& cameraId); // flashlight control sp mFlashlight; @@ -435,9 +638,6 @@ private: void onTorchStatusChangedLocked(const String8& cameraId, ICameraServiceListener::TorchStatus newStatus); - // validate the camera id for use of setting a torch mode. - bool validCameraIdForSetTorchMode(const String8& cameraId); - // get a camera's torch status. mTorchStatusMutex should be locked. status_t getTorchStatusLocked(const String8 &cameraId, ICameraServiceListener::TorchStatus *status) const; @@ -451,19 +651,9 @@ private: // Helpers - bool isValidCameraId(int cameraId); - bool setUpVendorTags(); /** - * A mapping of camera ids to CameraParameters returned by that camera device. - * - * This cache is used to generate CameraCharacteristic metadata when using - * the HAL1 shim. - */ - KeyedVector mShimParams; - - /** * Initialize and cache the metadata used by the HAL1 shim for a given cameraId. * * Returns OK on success, or a negative error code. @@ -486,25 +676,190 @@ private: */ status_t generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo); + static int getCallingPid(); + + static int getCallingUid(); + /** - * Connect a new camera client. This should only be used while holding the - * mutex for mServiceLock. - * - * Returns OK on success, or a negative error code. + * Get the current system time as a formatted string. */ - status_t connectHelperLocked( - /*out*/ - sp& client, - /*in*/ - const sp& cameraClient, - int cameraId, - const String16& clientPackageName, - int clientUid, - int callingPid, - int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED, - bool legacyMode = false); + static String8 getFormattedCurrentTime(); + + /** + * Get the camera eviction priority from the current process state given by ActivityManager. + */ + static int getCameraPriorityFromProcState(int procState); + + static status_t makeClient(const sp& cameraService, + const sp& cameraCb, const String16& packageName, const String8& cameraId, + int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode, + int halVersion, int deviceVersion, apiLevel effectiveApiLevel, + /*out*/sp* client); }; +template +void CameraService::CameraState::updateStatus(ICameraServiceListener::Status status, + const String8& cameraId, + std::initializer_list rejectSourceStates, + Func onStatusUpdatedLocked) { + Mutex::Autolock lock(mStatusLock); + ICameraServiceListener::Status oldStatus = mStatus; + mStatus = status; + + if (oldStatus == status) { + return; + } + + ALOGV("%s: Status has changed for camera ID %s from %#x to %#x", __FUNCTION__, + cameraId.string(), oldStatus, status); + + if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT && + (status != ICameraServiceListener::STATUS_PRESENT && + status != ICameraServiceListener::STATUS_ENUMERATING)) { + + ALOGW("%s: From NOT_PRESENT can only transition into PRESENT or ENUMERATING", + __FUNCTION__); + mStatus = oldStatus; + return; + } + + /** + * Sometimes we want to conditionally do a transition. + * For example if a client disconnects, we want to go to PRESENT + * only if we weren't already in NOT_PRESENT or ENUMERATING. + */ + for (auto& rejectStatus : rejectSourceStates) { + if (oldStatus == rejectStatus) { + ALOGV("%s: Rejecting status transition for Camera ID %s, since the source " + "state was was in one of the bad states.", __FUNCTION__, cameraId.string()); + mStatus = oldStatus; + return; + } + } + + onStatusUpdatedLocked(cameraId, status); +} + + +template +status_t CameraService::connectHelper(const sp& cameraCb, const String8& cameraId, + int halVersion, const String16& clientPackageName, int clientUid, + apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly, + /*out*/sp& device) { + status_t ret = NO_ERROR; + String8 clientName8(clientPackageName); + int clientPid = getCallingPid(); + + ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and " + "Camera API version %d", clientPid, clientName8.string(), cameraId.string(), + halVersion, static_cast(effectiveApiLevel)); + + // Enforce client permissions and do basic sanity checks + if((ret = validateConnect(cameraId, /*inout*/clientUid)) != NO_ERROR) { + return ret; + } + + sp client = nullptr; + { + // Acquire mServiceLock and prevent other clients from connecting + std::unique_ptr lock = + AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS); + if (lock == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (too many other clients connecting)." + , clientPid); + return -EBUSY; + } + + // Check the shim parameters after acquiring lock, if they have already been updated and + // we were doing a shim update, return immediately + if (shimUpdateOnly) { + auto cameraState = getCameraState(cameraId); + if (cameraState != nullptr) { + if (!cameraState->getShimParams().isEmpty()) return NO_ERROR; + } + } + + sp clientTmp = nullptr; + std::shared_ptr>> partial; + if ((ret = handleEvictionsLocked(cameraId, clientPid, effectiveApiLevel, + IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp, + /*out*/&partial)) != NO_ERROR) { + return ret; + } + + if (clientTmp.get() != nullptr) { + // Handle special case for API1 MediaRecorder where the existing client is returned + device = static_cast(clientTmp.get()); + return NO_ERROR; + } + + // give flashlight a chance to close devices if necessary. + mFlashlight->prepareDeviceOpen(cameraId); + + // TODO: Update getDeviceVersion + HAL interface to use strings for Camera IDs + int id = cameraIdToInt(cameraId); + if (id == -1) { + ALOGE("%s: Invalid camera ID %s, cannot get device version from HAL.", __FUNCTION__, + cameraId.string()); + return BAD_VALUE; + } + + int facing = -1; + int deviceVersion = getDeviceVersion(id, /*out*/&facing); + sp tmp = nullptr; + if((ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid, + clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel, + /*out*/&tmp)) != NO_ERROR) { + return ret; + } + client = static_cast(tmp.get()); + + LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state", + __FUNCTION__); + + if ((ret = client->initialize(mModule)) != OK) { + ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__); + return ret; + } + + sp remoteCallback = client->getRemote(); + if (remoteCallback != nullptr) { + remoteCallback->linkToDeath(this); + } + + // Update shim paremeters for legacy clients + if (effectiveApiLevel == API_1) { + // Assume we have always received a Client subclass for API1 + sp shimClient = reinterpret_cast(client.get()); + String8 rawParams = shimClient->getParameters(); + CameraParameters params(rawParams); + + auto cameraState = getCameraState(cameraId); + if (cameraState != nullptr) { + cameraState->setShimParams(params); + } else { + ALOGE("%s: Cannot update shim parameters for camera %s, no such device exists.", + __FUNCTION__, cameraId.string()); + } + } + + if (shimUpdateOnly) { + // If only updating legacy shim parameters, immediately disconnect client + mServiceLock.unlock(); + client->disconnect(); + mServiceLock.lock(); + } else { + // Otherwise, add client to active clients list + finishConnectLocked(client, partial); + } + } // lock is destroyed, allow further connect calls + + // Important: release the mutex here so the client can call back into the service from its + // destructor (can be at the end of the call) + device = client; + return NO_ERROR; +} + } // namespace android #endif diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index 5dbdeb2..6f44aee 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -163,11 +163,9 @@ Camera2Client::~Camera2Client() { status_t Camera2Client::dump(int fd, const Vector& args) { String8 result; - result.appendFormat("Client2[%d] (%p) Client: %s PID: %d, dump:\n", - mCameraId, + result.appendFormat("Client2[%d] (%p) PID: %d, dump:\n", mCameraId, (getRemoteCallback() != NULL ? (IInterface::asBinder(getRemoteCallback()).get()) : NULL), - String8(mClientPackageName).string(), mClientPid); result.append(" State: "); #define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break; diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp index 6bea3b6..e552633 100644 --- a/services/camera/libcameraservice/api1/CameraClient.cpp +++ b/services/camera/libcameraservice/api1/CameraClient.cpp @@ -99,12 +99,7 @@ status_t CameraClient::initialize(CameraModule *module) { // tear down the client CameraClient::~CameraClient() { - // this lock should never be NULL - Mutex* lock = mCameraService->getClientLockById(mCameraId); - lock->lock(); mDestructionStarted = true; - // client will not be accessed from callback. should unlock to prevent dead-lock in disconnect - lock->unlock(); int callingPid = getCallingPid(); LOG1("CameraClient::~CameraClient E (pid %d, this %p)", callingPid, this); @@ -116,11 +111,11 @@ status_t CameraClient::dump(int fd, const Vector& args) { const size_t SIZE = 256; char buffer[SIZE]; - size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) PID: %d\n", + size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) with UID %d\n", mCameraId, (getRemoteCallback() != NULL ? IInterface::asBinder(getRemoteCallback()).get() : NULL), - mClientPid); + mClientUid); len = (len > SIZE - 1) ? SIZE - 1 : len; write(fd, buffer, len); @@ -677,6 +672,13 @@ bool CameraClient::lockIfMessageWanted(int32_t msgType) { LOG1("lockIfMessageWanted(%d): waited for %d ms", msgType, sleepCount * CHECK_MESSAGE_INTERVAL); } + + // If messages are no longer enabled after acquiring lock, release and drop message + if ((mMsgEnabled & msgType) == 0) { + mLock.unlock(); + break; + } + return true; } if (sleepCount++ == 0) { @@ -702,26 +704,13 @@ bool CameraClient::lockIfMessageWanted(int32_t msgType) { // (others) c->dataCallback // dataCallbackTimestamp // (others) c->dataCallbackTimestamp -// -// NOTE: the *Callback functions grab mLock of the client before passing -// control to handle* functions. So the handle* functions must release the -// lock before calling the ICameraClient's callbacks, so those callbacks can -// invoke methods in the Client class again (For example, the preview frame -// callback may want to releaseRecordingFrame). The handle* functions must -// release the lock after all accesses to member variables, so it must be -// handled very carefully. void CameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user) { LOG2("notifyCallback(%d)", msgType); - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - CameraClient* client = - static_cast(getClientFromCookie(user)); - if (client == NULL) return; + sp client = static_cast(getClientFromCookie(user).get()); + if (client.get() == nullptr) return; if (!client->lockIfMessageWanted(msgType)) return; @@ -740,13 +729,8 @@ void CameraClient::dataCallback(int32_t msgType, const sp& dataPtr, camera_frame_metadata_t *metadata, void* user) { LOG2("dataCallback(%d)", msgType); - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - CameraClient* client = - static_cast(getClientFromCookie(user)); - if (client == NULL) return; + sp client = static_cast(getClientFromCookie(user).get()); + if (client.get() == nullptr) return; if (!client->lockIfMessageWanted(msgType)) return; if (dataPtr == 0 && metadata == NULL) { @@ -778,13 +762,8 @@ void CameraClient::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp& dataPtr, void* user) { LOG2("dataCallbackTimestamp(%d)", msgType); - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - CameraClient* client = - static_cast(getClientFromCookie(user)); - if (client == NULL) return; + sp client = static_cast(getClientFromCookie(user).get()); + if (client.get() == nullptr) return; if (!client->lockIfMessageWanted(msgType)) return; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index acc092c..755628b 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -586,9 +586,7 @@ status_t CameraDeviceClient::dump(int fd, const Vector& args) { mCameraId, (getRemoteCallback() != NULL ? IInterface::asBinder(getRemoteCallback()).get() : NULL) ); - result.appendFormat(" Current client: %s (PID %d, UID %u)\n", - String8(mClientPackageName).string(), - mClientPid, mClientUid); + result.appendFormat(" Current client UID %u\n", mClientUid); result.append(" State:\n"); result.appendFormat(" Request ID counter: %d\n", mRequestIdCounter); diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.cpp b/services/camera/libcameraservice/utils/AutoConditionLock.cpp new file mode 100644 index 0000000..c8ee965 --- /dev/null +++ b/services/camera/libcameraservice/utils/AutoConditionLock.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AutoConditionLock.h" + +namespace android { + +WaitableMutexWrapper::WaitableMutexWrapper(Mutex* mutex) : mMutex{mutex}, mState{false} {} + +WaitableMutexWrapper::~WaitableMutexWrapper() {} + +// Locks manager-owned mutex +AutoConditionLock::AutoConditionLock(const std::shared_ptr& manager) : + mManager{manager}, mAutoLock{manager->mMutex} {} + +// Unlocks manager-owned mutex +AutoConditionLock::~AutoConditionLock() { + // Unset the condition and wake everyone up before releasing lock + mManager->mState = false; + mManager->mCondition.broadcast(); +} + +std::unique_ptr AutoConditionLock::waitAndAcquire( + const std::shared_ptr& manager, nsecs_t waitTime) { + + if (manager == nullptr || manager->mMutex == nullptr) { + // Bad input, return null + return std::unique_ptr{nullptr}; + } + + // Acquire scoped lock + std::unique_ptr scopedLock(new AutoConditionLock(manager)); + + // Figure out what time in the future we should hit the timeout + nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + waitTime; + + // Wait until we timeout, or success + while(manager->mState) { + status_t ret = manager->mCondition.waitRelative(*(manager->mMutex), waitTime); + if (ret != NO_ERROR) { + // Timed out or whatever, return null + return std::unique_ptr{nullptr}; + } + waitTime = failTime - systemTime(SYSTEM_TIME_MONOTONIC); + } + + // Set the condition and return + manager->mState = true; + return scopedLock; +} + +std::unique_ptr AutoConditionLock::waitAndAcquire( + const std::shared_ptr& manager) { + + if (manager == nullptr || manager->mMutex == nullptr) { + // Bad input, return null + return std::unique_ptr{nullptr}; + } + + // Acquire scoped lock + std::unique_ptr scopedLock(new AutoConditionLock(manager)); + + // Wait until we timeout, or success + while(manager->mState) { + status_t ret = manager->mCondition.wait(*(manager->mMutex)); + if (ret != NO_ERROR) { + // Timed out or whatever, return null + return std::unique_ptr{nullptr}; + } + } + + // Set the condition and return + manager->mState = true; + return scopedLock; +} + +}; // namespace android diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.h b/services/camera/libcameraservice/utils/AutoConditionLock.h new file mode 100644 index 0000000..9a3eafc --- /dev/null +++ b/services/camera/libcameraservice/utils/AutoConditionLock.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H +#define ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H + +#include +#include +#include +#include + +#include + +namespace android { + +/** + * WaitableMutexWrapper can be used with AutoConditionLock to construct scoped locks for the + * wrapped Mutex with timeouts for lock acquisition. + */ +class WaitableMutexWrapper { + friend class AutoConditionLock; +public: + /** + * Construct the ConditionManger with the given Mutex. + */ + WaitableMutexWrapper(Mutex* mutex); + + virtual ~WaitableMutexWrapper(); +private: + Mutex* mMutex; + bool mState; + Condition mCondition; +}; + +/** + * AutoConditionLock is a scoped lock similar to Mutex::Autolock, but allows timeouts to be + * specified for lock acquisition. + * + * AutoConditionLock is used with a WaitableMutexWrapper to lock/unlock the WaitableMutexWrapper's + * wrapped Mutex, and wait/set/signal the WaitableMutexWrapper's wrapped condition. To use this, + * call AutoConditionLock::waitAndAcquire to get an instance. This will: + * - Lock the given WaitableMutexWrapper's mutex. + * - Wait for the WaitableMutexWrapper's condition to become false, or timeout. + * - Set the WaitableMutexWrapper's condition to true. + * + * When the AutoConditionLock goes out of scope and is destroyed, this will: + * - Set the WaitableMutexWrapper's condition to false. + * - Signal threads waiting on this condition to wakeup. + * - Release WaitableMutexWrapper's mutex. + */ +class AutoConditionLock final { +public: + AutoConditionLock() = delete; + AutoConditionLock(const AutoConditionLock& other) = delete; + AutoConditionLock & operator=(const AutoConditionLock&) = delete; + + ~AutoConditionLock(); + + /** + * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting up to waitTime + * nanoseconds to acquire the WaitableMutexWrapper's wrapped lock. + * + * Return an empty unique_ptr if this fails, or a timeout occurs. + */ + static std::unique_ptr waitAndAcquire( + const std::shared_ptr& manager, nsecs_t waitTime); + + /** + * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting indefinitely to + * acquire the WaitableMutexWrapper's wrapped lock. + * + * Return an empty unique_ptr if this fails. + */ + static std::unique_ptr waitAndAcquire( + const std::shared_ptr& manager); +private: + AutoConditionLock(const std::shared_ptr& manager); + + std::shared_ptr mManager; + Mutex::Autolock mAutoLock; +}; + +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h new file mode 100644 index 0000000..ad5486d --- /dev/null +++ b/services/camera/libcameraservice/utils/ClientManager.h @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H +#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace android { +namespace resource_policy { + +// -------------------------------------------------------------------------------- + +/** + * The ClientDescriptor class is a container for a given key/value pair identifying a shared + * resource, and the corresponding cost, priority, owner ID, and conflicting keys list used + * in determining eviction behavior. + * + * Aside from the priority, these values are immutable once the ClientDescriptor has been + * constructed. + */ +template +class ClientDescriptor final { +public: + ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost, + const std::set& conflictingKeys, int32_t priority, int32_t ownerId); + ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set&& conflictingKeys, + int32_t priority, int32_t ownerId); + + ~ClientDescriptor(); + + /** + * Return the key for this descriptor. + */ + const KEY& getKey() const; + + /** + * Return the value for this descriptor. + */ + const VALUE& getValue() const; + + /** + * Return the cost for this descriptor. + */ + int32_t getCost() const; + + /** + * Return the priority for this descriptor. + */ + int32_t getPriority() const; + + /** + * Return the owner ID for this descriptor. + */ + int32_t getOwnerId() const; + + /** + * Return true if the given key is in this descriptor's conflicting keys list. + */ + bool isConflicting(const KEY& key) const; + + /** + * Return the set of all conflicting keys for this descriptor. + */ + std::set getConflicting() const; + + /** + * Set the proirity for this descriptor. + */ + void setPriority(int32_t priority); + + // This class is ordered by key + template + friend bool operator < (const ClientDescriptor& a, const ClientDescriptor& b); + +private: + KEY mKey; + VALUE mValue; + int32_t mCost; + std::set mConflicting; + int32_t mPriority; + int32_t mOwnerId; +}; // class ClientDescriptor + +template +bool operator < (const ClientDescriptor& a, const ClientDescriptor& b) { + return a.mKey < b.mKey; +} + +template +ClientDescriptor::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost, + const std::set& conflictingKeys, int32_t priority, int32_t ownerId) : mKey{key}, + mValue{value}, mCost{cost}, mConflicting{conflictingKeys}, mPriority{priority}, + mOwnerId{ownerId} {} + +template +ClientDescriptor::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, + std::set&& conflictingKeys, int32_t priority, int32_t ownerId) : + mKey{std::forward(key)}, mValue{std::forward(value)}, mCost{cost}, + mConflicting{std::forward>(conflictingKeys)}, mPriority{priority}, + mOwnerId{ownerId} {} + +template +ClientDescriptor::~ClientDescriptor() {} + +template +const KEY& ClientDescriptor::getKey() const { + return mKey; +} + +template +const VALUE& ClientDescriptor::getValue() const { + return mValue; +} + +template +int32_t ClientDescriptor::getCost() const { + return mCost; +} + +template +int32_t ClientDescriptor::getPriority() const { + return mPriority; +} + +template +int32_t ClientDescriptor::getOwnerId() const { + return mOwnerId; +} + +template +bool ClientDescriptor::isConflicting(const KEY& key) const { + if (key == mKey) return true; + for (const auto& x : mConflicting) { + if (key == x) return true; + } + return false; +} + +template +std::set ClientDescriptor::getConflicting() const { + return mConflicting; +} + +template +void ClientDescriptor::setPriority(int32_t priority) { + mPriority = priority; +} + +// -------------------------------------------------------------------------------- + +/** + * The ClientManager class wraps an LRU-ordered list of active clients and implements eviction + * behavior for handling shared resource access. + * + * When adding a new descriptor, eviction behavior is as follows: + * - Keys are unique, adding a descriptor with the same key as an existing descriptor will + * result in the lower-priority of the two being removed. Priority ties result in the + * LRU descriptor being evicted (this means the incoming descriptor be added in this case). + * - Any descriptors with keys that are in the incoming descriptor's 'conflicting keys' list + * will be removed if they have an equal or lower priority than the incoming descriptor; + * if any have a higher priority, the incoming descriptor is removed instead. + * - If the sum of all descriptors' costs, including the incoming descriptor's, is more than + * the max cost allowed for this ClientManager, descriptors with non-zero cost, equal or lower + * priority, and a different owner will be evicted in LRU order until either the cost is less + * than the max cost, or all descriptors meeting this criteria have been evicted and the + * incoming descriptor has the highest priority. Otherwise, the incoming descriptor is + * removed instead. + */ +template +class ClientManager { +public: + // The default maximum "cost" allowed before evicting + static constexpr int32_t DEFAULT_MAX_COST = 100; + + ClientManager(); + ClientManager(int32_t totalCost); + + /** + * Add a given ClientDescriptor to the managed list. ClientDescriptors for clients that + * are evicted by this action are returned in a vector. + * + * This may return the ClientDescriptor passed in if it would be evicted. + */ + std::vector>> addAndEvict( + const std::shared_ptr>& client); + + /** + * Given a map containing owner (pid) -> priority mappings, update the priority of each + * ClientDescriptor with an owner in this mapping. + */ + void updatePriorities(const std::map& ownerPriorityList); + + /** + * Remove all ClientDescriptors. + */ + void removeAll(); + + /** + * Remove and return the ClientDescriptor with a given key. + */ + std::shared_ptr> remove(const KEY& key); + + /** + * Remove the given ClientDescriptor. + */ + void remove(const std::shared_ptr>& value); + + /** + * Return a vector of the ClientDescriptors that would be evicted by adding the given + * ClientDescriptor. + * + * This may return the ClientDescriptor passed in if it would be evicted. + */ + std::vector>> wouldEvict( + const std::shared_ptr>& client) const; + + /** + * Return a vector of active ClientDescriptors that prevent this client from being added. + */ + std::vector>> getIncompatibleClients( + const std::shared_ptr>& client) const; + + /** + * Return a vector containing all currently active ClientDescriptors. + */ + std::vector>> getAll() const; + + /** + * Return a vector containing all keys of currently active ClientDescriptors. + */ + std::vector getAllKeys() const; + + /** + * Return a vector of the owner tags of all currently active ClientDescriptors (duplicates + * will be removed). + */ + std::vector getAllOwners() const; + + /** + * Return the ClientDescriptor corresponding to the given key, or an empty shared pointer + * if none exists. + */ + std::shared_ptr> get(const KEY& key) const; + +protected: + ~ClientManager(); + +private: + + /** + * Return a vector of the ClientDescriptors that would be evicted by adding the given + * ClientDescriptor. If returnIncompatibleClients is set to true, instead, return the + * vector of ClientDescriptors that are higher priority than the incoming client and + * either conflict with this client, or contribute to the resource cost if that would + * prevent the incoming client from being added. + * + * This may return the ClientDescriptor passed in. + */ + std::vector>> wouldEvictLocked( + const std::shared_ptr>& client, + bool returnIncompatibleClients = false) const; + + int64_t getCurrentCostLocked() const; + + mutable Mutex mLock; + int32_t mMaxCost; + // LRU ordered, most recent at end + std::vector>> mClients; +}; // class ClientManager + +template +ClientManager::ClientManager() : + ClientManager(DEFAULT_MAX_COST) {} + +template +ClientManager::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {} + +template +ClientManager::~ClientManager() {} + +template +std::vector>> ClientManager::wouldEvict( + const std::shared_ptr>& client) const { + Mutex::Autolock lock(mLock); + return wouldEvictLocked(client); +} + +template +std::vector>> +ClientManager::getIncompatibleClients( + const std::shared_ptr>& client) const { + Mutex::Autolock lock(mLock); + return wouldEvictLocked(client, /*returnIncompatibleClients*/true); +} + +template +std::vector>> +ClientManager::wouldEvictLocked( + const std::shared_ptr>& client, + bool returnIncompatibleClients) const { + + std::vector>> evictList; + + // Disallow null clients, return input + if (client == nullptr) { + evictList.push_back(client); + return evictList; + } + + const KEY& key = client->getKey(); + int32_t cost = client->getCost(); + int32_t priority = client->getPriority(); + int32_t owner = client->getOwnerId(); + + int64_t totalCost = getCurrentCostLocked() + cost; + + // Determine the MRU of the owners tied for having the highest priority + int32_t highestPriorityOwner = owner; + int32_t highestPriority = priority; + for (const auto& i : mClients) { + int32_t curPriority = i->getPriority(); + if (curPriority >= highestPriority) { + highestPriority = curPriority; + highestPriorityOwner = i->getOwnerId(); + } + } + + if (highestPriority == priority) { + // Switch back owner if the incoming client has the highest priority, as it is MRU + highestPriorityOwner = owner; + } + + // Build eviction list of clients to remove + for (const auto& i : mClients) { + const KEY& curKey = i->getKey(); + int32_t curCost = i->getCost(); + int32_t curPriority = i->getPriority(); + int32_t curOwner = i->getOwnerId(); + + bool conflicting = (curKey == key || i->isConflicting(key) || + client->isConflicting(curKey)); + + if (!returnIncompatibleClients) { + // Find evicted clients + + if (conflicting && curPriority > priority) { + // Pre-existing conflicting client with higher priority exists + evictList.clear(); + evictList.push_back(client); + return evictList; + } else if (conflicting || ((totalCost > mMaxCost && curCost > 0) && + (curPriority <= priority) && + !(highestPriorityOwner == owner && owner == curOwner))) { + // Add a pre-existing client to the eviction list if: + // - We are adding a client with higher priority that conflicts with this one. + // - The total cost including the incoming client's is more than the allowable + // maximum, and the client has a non-zero cost, lower priority, and a different + // owner than the incoming client when the incoming client has the + // highest priority. + evictList.push_back(i); + totalCost -= curCost; + } + } else { + // Find clients preventing the incoming client from being added + + if (curPriority > priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) { + // Pre-existing conflicting client with higher priority exists + evictList.push_back(i); + } + } + } + + // Immediately return the incompatible clients if we are calculating these instead + if (returnIncompatibleClients) { + return evictList; + } + + // If the total cost is too high, return the input unless the input has the highest priority + if (totalCost > mMaxCost && highestPriorityOwner != owner) { + evictList.clear(); + evictList.push_back(client); + return evictList; + } + + return evictList; + +} + +template +std::vector>> ClientManager::addAndEvict( + const std::shared_ptr>& client) { + Mutex::Autolock lock(mLock); + auto evicted = wouldEvictLocked(client); + auto it = evicted.begin(); + if (it != evicted.end() && *it == client) { + return evicted; + } + + auto iter = evicted.cbegin(); + + // Remove evicted clients from list + mClients.erase(std::remove_if(mClients.begin(), mClients.end(), + [&iter] (std::shared_ptr>& curClientPtr) { + if (curClientPtr->getKey() == (*iter)->getKey()) { + iter++; + return true; + } + return false; + }), mClients.end()); + + mClients.push_back(client); + + return evicted; +} + +template +std::vector>> +ClientManager::getAll() const { + Mutex::Autolock lock(mLock); + return mClients; +} + +template +std::vector ClientManager::getAllKeys() const { + Mutex::Autolock lock(mLock); + std::vector keys(mClients.size()); + for (const auto& i : mClients) { + keys.push_back(i->getKey()); + } + return keys; +} + +template +std::vector ClientManager::getAllOwners() const { + Mutex::Autolock lock(mLock); + std::set owners; + for (const auto& i : mClients) { + owners.emplace(i->getOwnerId()); + } + return std::vector(owners.begin(), owners.end()); +} + +template +void ClientManager::updatePriorities( + const std::map& ownerPriorityList) { + Mutex::Autolock lock(mLock); + for (auto& i : mClients) { + auto j = ownerPriorityList.find(i->getOwnerId()); + if (j != ownerPriorityList.end()) { + i->setPriority(j->second); + } + } +} + +template +std::shared_ptr> ClientManager::get( + const KEY& key) const { + Mutex::Autolock lock(mLock); + for (const auto& i : mClients) { + if (i->getKey() == key) return i; + } + return std::shared_ptr>(nullptr); +} + +template +void ClientManager::removeAll() { + Mutex::Autolock lock(mLock); + mClients.clear(); +} + +template +std::shared_ptr> ClientManager::remove(const KEY& key) { + Mutex::Autolock lock(mLock); + + std::shared_ptr> ret; + + // Remove evicted clients from list + mClients.erase(std::remove_if(mClients.begin(), mClients.end(), + [&key, &ret] (std::shared_ptr>& curClientPtr) { + if (curClientPtr->getKey() == key) { + ret = curClientPtr; + return true; + } + return false; + }), mClients.end()); + + return ret; +} + +template +void ClientManager::remove( + const std::shared_ptr>& value) { + Mutex::Autolock lock(mLock); + // Remove evicted clients from list + mClients.erase(std::remove_if(mClients.begin(), mClients.end(), + [&value] (std::shared_ptr>& curClientPtr) { + if (curClientPtr == value) { + return true; + } + return false; + }), mClients.end()); +} + +template +int64_t ClientManager::getCurrentCostLocked() const { + int64_t totalCost = 0; + for (const auto& x : mClients) { + totalCost += x->getCost(); + } + return totalCost; +} + +// -------------------------------------------------------------------------------- + +}; // namespace resource_policy +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H diff --git a/services/camera/libcameraservice/utils/RingBuffer.h b/services/camera/libcameraservice/utils/RingBuffer.h new file mode 100644 index 0000000..df7c00e --- /dev/null +++ b/services/camera/libcameraservice/utils/RingBuffer.h @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_SERVICE_UTILS_RING_BUFFER_H +#define ANDROID_SERVICE_UTILS_RING_BUFFER_H + +#include +#include + +#include +#include +#include + +namespace android { + +/** + * A RingBuffer class that maintains an array of objects that can grow up to a certain size. + * Elements added to the RingBuffer are inserted in the logical front of the buffer, and + * invalidate all current iterators for that RingBuffer object. + */ +template +class RingBuffer final { +public: + + /** + * Construct a RingBuffer that can grow up to the given length. + */ + RingBuffer(size_t length); + + /** + * Forward iterator to this class. Implements an std:forward_iterator. + */ + class iterator : public std::iterator { + public: + iterator(T* ptr, size_t size, size_t pos, size_t ctr); + + iterator& operator++(); + + iterator operator++(int); + + bool operator==(const iterator& rhs); + + bool operator!=(const iterator& rhs); + + T& operator*(); + + T* operator->(); + + private: + T* mPtr; + size_t mSize; + size_t mPos; + size_t mCtr; + }; + + /** + * Constant forward iterator to this class. Implements an std:forward_iterator. + */ + class const_iterator : public std::iterator { + public: + const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr); + + const_iterator& operator++(); + + const_iterator operator++(int); + + bool operator==(const const_iterator& rhs); + + bool operator!=(const const_iterator& rhs); + + const T& operator*(); + + const T* operator->(); + + private: + const T* mPtr; + size_t mSize; + size_t mPos; + size_t mCtr; + }; + + /** + * Adds item to the front of this RingBuffer. If the RingBuffer is at its maximum length, + * this will result in the last element being replaced (this is done using the element's + * assignment operator). + * + * All current iterators are invalidated. + */ + void add(const T& item); + + /** + * Moves item to the front of this RingBuffer. Following a call to this, item should no + * longer be used. If the RingBuffer is at its maximum length, this will result in the + * last element being replaced (this is done using the element's assignment operator). + * + * All current iterators are invalidated. + */ + void add(T&& item); + + /** + * Construct item in-place in the front of this RingBuffer using the given arguments. If + * the RingBuffer is at its maximum length, this will result in the last element being + * replaced (this is done using the element's assignment operator). + * + * All current iterators are invalidated. + */ + template + void emplace(Args&&... args); + + /** + * Get an iterator to the front of this RingBuffer. + */ + iterator begin(); + + /** + * Get an iterator to the end of this RingBuffer. + */ + iterator end(); + + /** + * Get a const_iterator to the front of this RingBuffer. + */ + const_iterator begin() const; + + /** + * Get a const_iterator to the end of this RingBuffer. + */ + const_iterator end() const; + + /** + * Return a reference to the element at a given index. If the index is out of range for + * this ringbuffer, [0, size), the behavior for this is undefined. + */ + T& operator[](size_t index); + + /** + * Return a const reference to the element at a given index. If the index is out of range + * for this ringbuffer, [0, size), the behavior for this is undefined. + */ + const T& operator[](size_t index) const; + + /** + * Return the current size of this RingBuffer. + */ + size_t size() const; + + /** + * Remove all elements from this RingBuffer and set the size to 0. + */ + void clear(); + +private: + size_t mFrontIdx; + size_t mMaxBufferSize; + std::vector mBuffer; +}; // class RingBuffer + + +template +RingBuffer::RingBuffer(size_t length) : mFrontIdx{0}, mMaxBufferSize{length} {} + +template +RingBuffer::iterator::iterator(T* ptr, size_t size, size_t pos, size_t ctr) : + mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {} + +template +typename RingBuffer::iterator& RingBuffer::iterator::operator++() { + ++mCtr; + + if (CC_UNLIKELY(mCtr == mSize)) { + mPos = mSize; + return *this; + } + + mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1); + return *this; +} + +template +typename RingBuffer::iterator RingBuffer::iterator::operator++(int) { + iterator tmp{mPtr, mSize, mPos, mCtr}; + ++(*this); + return tmp; +} + +template +bool RingBuffer::iterator::operator==(const iterator& rhs) { + return (mPtr + mPos) == (rhs.mPtr + rhs.mPos); +} + +template +bool RingBuffer::iterator::operator!=(const iterator& rhs) { + return (mPtr + mPos) != (rhs.mPtr + rhs.mPos); +} + +template +T& RingBuffer::iterator::operator*() { + return *(mPtr + mPos); +} + +template +T* RingBuffer::iterator::operator->() { + return mPtr + mPos; +} + +template +RingBuffer::const_iterator::const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr) : + mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {} + +template +typename RingBuffer::const_iterator& RingBuffer::const_iterator::operator++() { + ++mCtr; + + if (CC_UNLIKELY(mCtr == mSize)) { + mPos = mSize; + return *this; + } + + mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1); + return *this; +} + +template +typename RingBuffer::const_iterator RingBuffer::const_iterator::operator++(int) { + const_iterator tmp{mPtr, mSize, mPos, mCtr}; + ++(*this); + return tmp; +} + +template +bool RingBuffer::const_iterator::operator==(const const_iterator& rhs) { + return (mPtr + mPos) == (rhs.mPtr + rhs.mPos); +} + +template +bool RingBuffer::const_iterator::operator!=(const const_iterator& rhs) { + return (mPtr + mPos) != (rhs.mPtr + rhs.mPos); +} + +template +const T& RingBuffer::const_iterator::operator*() { + return *(mPtr + mPos); +} + +template +const T* RingBuffer::const_iterator::operator->() { + return mPtr + mPos; +} + +template +void RingBuffer::add(const T& item) { + if (mBuffer.size() < mMaxBufferSize) { + mBuffer.push_back(item); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); + return; + } + + mBuffer[mFrontIdx] = item; + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template +void RingBuffer::add(T&& item) { + if (mBuffer.size() != mMaxBufferSize) { + mBuffer.push_back(std::forward(item)); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); + return; + } + + // Only works for types with move assignment operator + mBuffer[mFrontIdx] = std::forward(item); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template +template +void RingBuffer::emplace(Args&&... args) { + if (mBuffer.size() != mMaxBufferSize) { + mBuffer.emplace_back(std::forward(args)...); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); + return; + } + + // Only works for types with move assignment operator + mBuffer[mFrontIdx] = T(std::forward(args)...); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template +typename RingBuffer::iterator RingBuffer::begin() { + size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1; + return iterator(mBuffer.data(), mBuffer.size(), (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0); +} + +template +typename RingBuffer::iterator RingBuffer::end() { + size_t s = mBuffer.size(); + return iterator(mBuffer.data(), s, s, s); +} + +template +typename RingBuffer::const_iterator RingBuffer::begin() const { + size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1; + return const_iterator(mBuffer.data(), mBuffer.size(), + (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0); +} + +template +typename RingBuffer::const_iterator RingBuffer::end() const { + size_t s = mBuffer.size(); + return const_iterator(mBuffer.data(), s, s, s); +} + +template +T& RingBuffer::operator[](size_t index) { + LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.", + index, mBuffer.size()); + size_t pos = (index >= mFrontIdx) ? + mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index; + return mBuffer[pos]; +} + +template +const T& RingBuffer::operator[](size_t index) const { + LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.", + index, mBuffer.size()); + size_t pos = (index >= mFrontIdx) ? + mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index; + return mBuffer[pos]; +} + +template +size_t RingBuffer::size() const { + return mBuffer.size(); +} + +template +void RingBuffer::clear() { + mBuffer.clear(); + mFrontIdx = 0; +} + +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_RING_BUFFER_H + + -- cgit v1.1