summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRuben Brunk <rubenbrunk@google.com>2015-03-19 05:55:16 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-03-19 05:55:16 +0000
commitd58b2ac7244a964caa3f4e386b8280ae2f5d2125 (patch)
tree84ef9b168bd3ab9e33c0532dabf33fe328eba334
parent377165c24968b044faccaf366a2377fa32143e2f (diff)
parentcc776718c0be7c31fe5ab4fc1446d377be60369f (diff)
downloadframeworks_av-d58b2ac7244a964caa3f4e386b8280ae2f5d2125.zip
frameworks_av-d58b2ac7244a964caa3f4e386b8280ae2f5d2125.tar.gz
frameworks_av-d58b2ac7244a964caa3f4e386b8280ae2f5d2125.tar.bz2
Merge "camera2: Add camera client eviction enforcement."
-rw-r--r--camera/CameraParameters.cpp4
-rw-r--r--include/camera/CameraParameters.h3
-rw-r--r--services/camera/libcameraservice/Android.mk1
-rw-r--r--services/camera/libcameraservice/CameraDeviceFactory.cpp1
-rw-r--r--services/camera/libcameraservice/CameraFlashlight.cpp14
-rw-r--r--services/camera/libcameraservice/CameraService.cpp1654
-rw-r--r--services/camera/libcameraservice/CameraService.h543
-rw-r--r--services/camera/libcameraservice/api1/Camera2Client.cpp4
-rw-r--r--services/camera/libcameraservice/api1/CameraClient.cpp51
-rw-r--r--services/camera/libcameraservice/api2/CameraDeviceClient.cpp4
-rw-r--r--services/camera/libcameraservice/utils/AutoConditionLock.cpp90
-rw-r--r--services/camera/libcameraservice/utils/AutoConditionLock.h99
-rw-r--r--services/camera/libcameraservice/utils/ClientManager.h539
-rw-r--r--services/camera/libcameraservice/utils/RingBuffer.h361
14 files changed, 2427 insertions, 941 deletions
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<int>& 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<CameraDeviceBase> 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 7a79750..0def668 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 <algorithm>
+#include <climits>
#include <stdio.h>
-#include <string.h>
+#include <cstring>
+#include <ctime>
+#include <string>
#include <sys/types.h>
+#include <inttypes.h>
#include <pthread.h>
#include <binder/AppOpsManager.h>
@@ -27,6 +32,7 @@
#include <binder/IServiceManager.h>
#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>
+#include <binder/ProcessInfoService.h>
#include <cutils/atomic.h>
#include <cutils/properties.h>
#include <cutils/multiuser.h>
@@ -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<CameraService> cs = const_cast<CameraService*>(
- static_cast<const CameraService*>(callbacks));
+ static_cast<const CameraService*>(callbacks));
- cs->onDeviceStatusChanged(
- camera_id,
- new_status);
+ cs->onDeviceStatusChanged(static_cast<camera_device_status_t>(camera_id),
+ static_cast<camera_device_status_t>(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<WaitableMutexWrapper>(&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<String8> 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<CameraState>(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<CameraState> 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<ICameraServiceListener::Status>(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<sp<BasicClient> > clientsToDisconnect;
+ sp<BasicClient> clientToDisconnect;
{
- Mutex::Autolock al(mServiceLock);
-
- /* Remove cached parameters from shim cache */
- mShimParams.removeItem(cameraId);
-
- /* Find all clients that we need to disconnect */
- sp<BasicClient> 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<ProClient> 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<BasicClient> 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<ICameraServiceListener::Status>(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<ICameraServiceListener::Status>(newStatus), id);
+ }
}
@@ -304,9 +313,11 @@ void CameraService::onTorchStatusChangedLocked(const String8& cameraId,
return;
}
- Vector<sp<ICameraServiceListener> >::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<VendorTagDescriptor>& 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>& cameraService,
+ const sp<IInterface>& 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<BasicClient>* client) {
- bool needsNewClient = false;
- sp<Client> 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<Client*>(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<ICameraClient> tmp = static_cast<ICameraClient*>(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<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
+ *client = new Camera2Client(cameraService, tmp, packageName, id, facing,
+ clientPid, clientUid, servicePid, legacyMode);
+ } else { // Camera2 API route
+ sp<ICameraDeviceCallbacks> tmp =
+ static_cast<ICameraDeviceCallbacks*>(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<ICameraClient> tmp = static_cast<ICameraClient*>(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<Client> tmp = nullptr;
+ if ((ret = connectHelper<ICameraClient,Client>(sp<ICameraClient>{nullptr}, id,
+ static_cast<int>(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<IBinder>& remoteCallback,
- sp<BasicClient> &client) {
- String8 clientName8(clientPackageName);
- int callingPid = getCallingPid();
+void CameraService::finishConnectLocked(const sp<BasicClient>& 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<int>(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<IBinder>& remoteCallback, const String8& packageName,
/*out*/
- sp<Client>& client,
- /*in*/
- const sp<ICameraClient>& cameraClient,
- int cameraId,
- const String16& clientPackageName,
- int clientUid,
- int callingPid,
- int halVersion,
- bool legacyMode) {
+ sp<BasicClient>* client,
+ std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) {
- // give flashlight a chance to close devices if necessary.
- mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId));
+ status_t ret = NO_ERROR;
+ std::vector<sp<BasicClient>> 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<int> ownerPids(mActiveClientManager.getAllOwners());
+ ownerPids.push_back(clientPid);
+
+ std::vector<int> 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<int,int> 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<BasicClient>{nullptr}, static_cast<int32_t>(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<BasicClient> 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<BasicClient> 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<ICamera>& 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> client;
- {
- Mutex::Autolock lock(mServiceLock);
- sp<BasicClient> clientTmp;
- if (!canConnectUnsafe(cameraId, clientPackageName,
- IInterface::asBinder(cameraClient),
- /*out*/clientTmp)) {
- return -EBUSY;
- } else if (client.get() != NULL) {
- device = static_cast<Client*>(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> client = nullptr;
+ ret = connectHelper<ICameraClient,Client>(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> client;
- {
- Mutex::Autolock lock(mServiceLock);
- sp<BasicClient> clientTmp;
- if (!canConnectUnsafe(cameraId, clientPackageName,
- IInterface::asBinder(cameraClient),
- /*out*/clientTmp)) {
- return -EBUSY;
- } else if (client.get() != NULL) {
- device = static_cast<Client*>(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> client = nullptr;
+ ret = connectHelper<ICameraClient,Client>(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<IProCameraCallbacks>& cameraCb,
+ int cameraId,
+ const String16& clientPackageName,
+ int clientUid,
+ /*out*/
+ sp<IProCameraUser>& 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<ICameraDeviceCallbacks>& cameraCb,
+ int cameraId,
+ const String16& clientPackageName,
+ int clientUid,
+ /*out*/
+ sp<ICameraDeviceUser>& device) {
+
+ status_t ret = NO_ERROR;
+ String8 id = String8::format("%d", cameraId);
+ sp<CameraDeviceClient> client = nullptr;
+ ret = connectHelper<ICameraDeviceCallbacks,CameraDeviceClient>(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<BasicClient>& client,
- const sp<IBinder>& 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<IProCameraCallbacks>& cameraCb,
- int cameraId,
- const String16& clientPackageName,
- int clientUid,
- /*out*/
- sp<IProCameraUser>& 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<ProClient> client;
- {
- Mutex::Autolock lock(mServiceLock);
- {
- sp<BasicClient> 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<ICameraDeviceCallbacks>& cameraCb,
- int cameraId,
- const String16& clientPackageName,
- int clientUid,
- /*out*/
- sp<ICameraDeviceUser>& 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<CameraDeviceClient> client;
- {
- Mutex::Autolock lock(mServiceLock);
- {
- sp<BasicClient> 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<ICameraServiceListener>& listener) {
ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
@@ -1201,23 +1182,29 @@ status_t CameraService::addListener(
Mutex::Autolock lock(mServiceLock);
- Vector<sp<ICameraServiceListener> >::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<ICameraServiceListener>& listener) {
+
+status_t CameraService::removeListener(const sp<ICameraServiceListener>& 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<sp<ICameraServiceListener> >::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<IBinder>& 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<BasicClient> client = findClientUnsafe(remoteBinder, outIndex);
-
- if (client != 0) {
- // Found our camera, clear and leave.
- LOG1("removeClient: clear camera %d", outIndex);
-
- sp<IBinder> remote = client->getRemote();
- if (remote != NULL) {
- remote->unlinkToDeath(this);
- }
-
- mClient[outIndex].clear();
- } else {
-
- sp<ProClient> 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::ProClient> CameraService::findProClientUnsafe(
- const wp<IBinder>& cameraCallbacksRemote)
-{
- sp<ProClient> clientPro;
-
- for (int i = 0; i < mNumberOfCameras; ++i) {
- Vector<size_t> removeIdx;
+bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) {
+ const int callingPid = getCallingPid();
+ const int servicePid = getpid();
+ bool ret = false;
+ {
+ // Acquire mServiceLock and prevent other clients from connecting
+ std::unique_ptr<AutoConditionLock> lock =
+ AutoConditionLock::waitAndAcquire(mServiceLockWrapper);
- for (size_t j = 0; j < mProClientList[i].size(); ++j) {
- wp<ProClient> cl = mProClientList[i][j];
- sp<ProClient> 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<sp<BasicClient>> 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::BasicClient> CameraService::findClientUnsafe(
- const wp<IBinder>& cameraClient, int& outIndex) {
- sp<BasicClient> 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::CameraState> CameraService::getCameraState(
+ const String8& cameraId) const {
+ std::shared_ptr<CameraState> 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::BasicClient> 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<BasicClient>{nullptr};
+ }
-Mutex* CameraService::getClientLockById(int cameraId) {
- if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL;
- return &mClientLock[cameraId];
+ return clientDescriptorPtr->getValue();
}
-sp<CameraService::BasicClient> CameraService::getClientByRemote(
- const wp<IBinder>& cameraClient) {
- // Declare this before the lock to make absolutely sure the
- // destructor won't be called with the lock held.
- sp<BasicClient> 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>& 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>& 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<IBinder> 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<Client*>(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> 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<Client>{
+ static_cast<Client*>(clientDescriptor->getValue().get())};
+ }
+ return sp<Client>{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<BasicClient> client):
@@ -1784,6 +1733,104 @@ void CameraService::ProClient::notifyError(ICameraDeviceCallbacks::CameraErrorCo
}
// ----------------------------------------------------------------------------
+// CameraState
+// ----------------------------------------------------------------------------
+
+CameraService::CameraState::CameraState(const String8& id, int cost,
+ const std::set<String8>& 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<String8> CameraService::CameraState::getConflicting() const {
+ return mConflicting;
+}
+
+String8 CameraService::CameraState::getId() const {
+ return mId;
+}
+
+// ----------------------------------------------------------------------------
+// CameraClientManager
+// ----------------------------------------------------------------------------
+
+CameraService::CameraClientManager::~CameraClientManager() {}
+
+sp<CameraService::BasicClient> CameraService::CameraClientManager::getCameraClient(
+ const String8& id) const {
+ auto descriptor = get(id);
+ if (descriptor == nullptr) {
+ return sp<BasicClient>{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<BasicClient>& value, int32_t cost,
+ const std::set<String8>& conflictingKeys, int32_t priority, int32_t ownerId) {
+
+ return std::make_shared<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>(
+ key, value, cost, conflictingKeys, priority, ownerId);
+}
+
+CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor(
+ const sp<BasicClient>& 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<String16>& 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<VendorTagDescriptor> desc = VendorTagDescriptor::getGlobalVendorTagDescriptor();
if (desc == NULL) {
@@ -1845,11 +1895,31 @@ status_t CameraService::dump(int fd, const Vector<String16>& 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<String16>& 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<String16>& 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<BasicClient> 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<String16>& args) {
write(fd, result.string(), result.size());
}
}
-
}
return NO_ERROR;
}
@@ -1931,124 +2031,68 @@ void CameraService::handleTorchClientBinderDied(const wp<IBinder> &who) {
}
}
-/*virtual*/void CameraService::binderDied(
- const wp<IBinder> &who) {
+/*virtual*/void CameraService::binderDied(const wp<IBinder> &who) {
/**
* While tempting to promote the wp<IBinder> 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<BasicClient> 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<ICameraServiceListener::Status> 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<wp<ProClient> > proClients(mProClientList[cameraId]);
- Vector<wp<ProClient> >::const_iterator it;
-
- for (it = proClients.begin(); it != proClients.end(); ++it) {
- sp<ProClient> 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<sp<ICameraServiceListener> >::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 <camera/ICameraServiceListener.h>
#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 <set>
+#include <string>
+#include <map>
+#include <memory>
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<T>
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<IBinder>& 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<String16>& 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>& cameraService,
const sp<IBinder>& 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<IBinder> 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<CameraService::Client> 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<IProCameraCallbacks> mRemoteCallback;
}; // class ProClient
+ typedef std::shared_ptr<resource_policy::ClientDescriptor<String8,
+ sp<CameraService::BasicClient>>> 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<String8, sp<CameraService::BasicClient>> {
+ public:
+ virtual ~CameraClientManager();
+
+ /**
+ * Return a strong pointer to the active BasicClient for this camera ID, or an empty
+ * if none exists.
+ */
+ sp<CameraService::BasicClient> 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<BasicClient>& value,
+ int32_t cost, const std::set<String8>& 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<BasicClient>& 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<String8>& 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<class Func>
+ void updateStatus(ICameraServiceListener::Status status, const String8& cameraId,
+ std::initializer_list<ICameraServiceListener::Status> 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<String8> 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<String8> 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<IBinder>& remoteCallback,
- /*out*/
- sp<BasicClient> &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<IBinder>& remoteCallback, const String8& packageName,
+ /*out*/
+ sp<BasicClient>* client,
+ std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial);
- // When connection is successful, initialize client and track its death
- status_t connectFinishUnsafe(const sp<BasicClient>& client,
- const sp<IBinder>& remoteCallback);
+ // Single implementation shared between the various connect calls
+ template<class CALLBACK, class CLIENT>
+ status_t connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId, int halVersion,
+ const String16& clientPackageName, int clientUid, apiLevel effectiveApiLevel,
+ bool legacyMode, bool shimUpdateOnly, /*out*/sp<CLIENT>& device);
- virtual sp<BasicClient> getClientByRemote(const wp<IBinder>& cameraClient);
+ // Lock guarding camera service state
Mutex mServiceLock;
- // either a Client or CameraDeviceClient
- wp<BasicClient> mClient[MAX_CAMERAS]; // protected by mServiceLock
- Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks
- int mNumberOfCameras;
- typedef wp<ProClient> weak_pro_client_ptr;
- Vector<weak_pro_client_ptr> mProClientList[MAX_CAMERAS];
+ // Condition to use with mServiceLock, used to handle simultaneous connect calls from clients
+ std::shared_ptr<WaitableMutexWrapper> 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<BasicClient> findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex);
- sp<ProClient> findProClientUnsafe(
- const wp<IBinder>& cameraCallbacksRemote);
+ // Mapping from camera ID -> state for each device, map is protected by mCameraStatesLock
+ std::map<String8, std::shared_ptr<CameraState>> 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<String8> mEventLog;
+
+ /**
+ * Get the camera state for a given camera id.
+ *
+ * This acquires mCameraStatesLock.
+ */
+ std::shared_ptr<CameraService::CameraState> 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<IBinder>& 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<BasicClient>& 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<CameraService::BasicClient> 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<sp<ICameraServiceListener> >
- mListenerList;
-
- // guard only mStatusList and the broadcasting of ICameraServiceListener
- mutable Mutex mStatusMutex;
- ICameraServiceListener::Status
- mStatusList[MAX_CAMERAS];
+ // Guarded by mStatusListenerMutex
+ std::vector<sp<ICameraServiceListener>> mListenerList;
+ Mutex mStatusListenerLock;
- // Read the current status (locks mStatusMutex)
- ICameraServiceListener::Status
- getStatus(int cameraId) const;
-
- typedef Vector<ICameraServiceListener::Status> 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<ICameraServiceListener::Status> rejectedSourceStates);
+ void updateStatus(ICameraServiceListener::Status status, const String8& cameraId);
// flashlight control
sp<CameraFlashlight> 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<int, CameraParameters> 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>& client,
- /*in*/
- const sp<ICameraClient>& 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>& cameraService,
+ const sp<IInterface>& 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<BasicClient>* client);
};
+template<class Func>
+void CameraService::CameraState::updateStatus(ICameraServiceListener::Status status,
+ const String8& cameraId,
+ std::initializer_list<ICameraServiceListener::Status> 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<class CALLBACK, class CLIENT>
+status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
+ int halVersion, const String16& clientPackageName, int clientUid,
+ apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
+ /*out*/sp<CLIENT>& 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<int>(effectiveApiLevel));
+
+ // Enforce client permissions and do basic sanity checks
+ if((ret = validateConnect(cameraId, /*inout*/clientUid)) != NO_ERROR) {
+ return ret;
+ }
+
+ sp<CLIENT> client = nullptr;
+ {
+ // Acquire mServiceLock and prevent other clients from connecting
+ std::unique_ptr<AutoConditionLock> 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<BasicClient> clientTmp = nullptr;
+ std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> 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<CLIENT*>(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<BasicClient> 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<CLIENT*>(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<IBinder> 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<Client> shimClient = reinterpret_cast<Client*>(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<String16>& 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<String16>& 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<CameraClient*>(getClientFromCookie(user));
- if (client == NULL) return;
+ sp<CameraClient> client = static_cast<CameraClient*>(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<IMemory>& 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<CameraClient*>(getClientFromCookie(user));
- if (client == NULL) return;
+ sp<CameraClient> client = static_cast<CameraClient*>(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<IMemory>& dataPtr, void* user) {
LOG2("dataCallbackTimestamp(%d)", msgType);
- Mutex* lock = getClientLockFromCookie(user);
- if (lock == NULL) return;
- Mutex::Autolock alock(*lock);
-
- CameraClient* client =
- static_cast<CameraClient*>(getClientFromCookie(user));
- if (client == NULL) return;
+ sp<CameraClient> client = static_cast<CameraClient*>(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 dde1779..ad10bda 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -595,9 +595,7 @@ status_t CameraDeviceClient::dump(int fd, const Vector<String16>& 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<WaitableMutexWrapper>& 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> AutoConditionLock::waitAndAcquire(
+ const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime) {
+
+ if (manager == nullptr || manager->mMutex == nullptr) {
+ // Bad input, return null
+ return std::unique_ptr<AutoConditionLock>{nullptr};
+ }
+
+ // Acquire scoped lock
+ std::unique_ptr<AutoConditionLock> 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<AutoConditionLock>{nullptr};
+ }
+ waitTime = failTime - systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+
+ // Set the condition and return
+ manager->mState = true;
+ return scopedLock;
+}
+
+std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire(
+ const std::shared_ptr<WaitableMutexWrapper>& manager) {
+
+ if (manager == nullptr || manager->mMutex == nullptr) {
+ // Bad input, return null
+ return std::unique_ptr<AutoConditionLock>{nullptr};
+ }
+
+ // Acquire scoped lock
+ std::unique_ptr<AutoConditionLock> 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<AutoConditionLock>{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 <utils/Timers.h>
+#include <utils/Condition.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+
+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<AutoConditionLock> waitAndAcquire(
+ const std::shared_ptr<WaitableMutexWrapper>& 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<AutoConditionLock> waitAndAcquire(
+ const std::shared_ptr<WaitableMutexWrapper>& manager);
+private:
+ AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager);
+
+ std::shared_ptr<WaitableMutexWrapper> 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 <utils/Mutex.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <set>
+#include <map>
+#include <memory>
+
+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 KEY, class VALUE>
+class ClientDescriptor final {
+public:
+ ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
+ const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId);
+ ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set<KEY>&& 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<KEY> getConflicting() const;
+
+ /**
+ * Set the proirity for this descriptor.
+ */
+ void setPriority(int32_t priority);
+
+ // This class is ordered by key
+ template<class K, class V>
+ friend bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b);
+
+private:
+ KEY mKey;
+ VALUE mValue;
+ int32_t mCost;
+ std::set<KEY> mConflicting;
+ int32_t mPriority;
+ int32_t mOwnerId;
+}; // class ClientDescriptor
+
+template<class K, class V>
+bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b) {
+ return a.mKey < b.mKey;
+}
+
+template<class KEY, class VALUE>
+ClientDescriptor<KEY, VALUE>::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
+ const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId) : mKey{key},
+ mValue{value}, mCost{cost}, mConflicting{conflictingKeys}, mPriority{priority},
+ mOwnerId{ownerId} {}
+
+template<class KEY, class VALUE>
+ClientDescriptor<KEY, VALUE>::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost,
+ std::set<KEY>&& conflictingKeys, int32_t priority, int32_t ownerId) :
+ mKey{std::forward<KEY>(key)}, mValue{std::forward<VALUE>(value)}, mCost{cost},
+ mConflicting{std::forward<std::set<KEY>>(conflictingKeys)}, mPriority{priority},
+ mOwnerId{ownerId} {}
+
+template<class KEY, class VALUE>
+ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {}
+
+template<class KEY, class VALUE>
+const KEY& ClientDescriptor<KEY, VALUE>::getKey() const {
+ return mKey;
+}
+
+template<class KEY, class VALUE>
+const VALUE& ClientDescriptor<KEY, VALUE>::getValue() const {
+ return mValue;
+}
+
+template<class KEY, class VALUE>
+int32_t ClientDescriptor<KEY, VALUE>::getCost() const {
+ return mCost;
+}
+
+template<class KEY, class VALUE>
+int32_t ClientDescriptor<KEY, VALUE>::getPriority() const {
+ return mPriority;
+}
+
+template<class KEY, class VALUE>
+int32_t ClientDescriptor<KEY, VALUE>::getOwnerId() const {
+ return mOwnerId;
+}
+
+template<class KEY, class VALUE>
+bool ClientDescriptor<KEY, VALUE>::isConflicting(const KEY& key) const {
+ if (key == mKey) return true;
+ for (const auto& x : mConflicting) {
+ if (key == x) return true;
+ }
+ return false;
+}
+
+template<class KEY, class VALUE>
+std::set<KEY> ClientDescriptor<KEY, VALUE>::getConflicting() const {
+ return mConflicting;
+}
+
+template<class KEY, class VALUE>
+void ClientDescriptor<KEY, VALUE>::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 KEY, class VALUE>
+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<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> addAndEvict(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& 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<int32_t,int32_t>& ownerPriorityList);
+
+ /**
+ * Remove all ClientDescriptors.
+ */
+ void removeAll();
+
+ /**
+ * Remove and return the ClientDescriptor with a given key.
+ */
+ std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key);
+
+ /**
+ * Remove the given ClientDescriptor.
+ */
+ void remove(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& 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<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvict(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
+
+ /**
+ * Return a vector of active ClientDescriptors that prevent this client from being added.
+ */
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getIncompatibleClients(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
+
+ /**
+ * Return a vector containing all currently active ClientDescriptors.
+ */
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getAll() const;
+
+ /**
+ * Return a vector containing all keys of currently active ClientDescriptors.
+ */
+ std::vector<KEY> getAllKeys() const;
+
+ /**
+ * Return a vector of the owner tags of all currently active ClientDescriptors (duplicates
+ * will be removed).
+ */
+ std::vector<int32_t> getAllOwners() const;
+
+ /**
+ * Return the ClientDescriptor corresponding to the given key, or an empty shared pointer
+ * if none exists.
+ */
+ std::shared_ptr<ClientDescriptor<KEY, VALUE>> 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<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvictLocked(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
+ bool returnIncompatibleClients = false) const;
+
+ int64_t getCurrentCostLocked() const;
+
+ mutable Mutex mLock;
+ int32_t mMaxCost;
+ // LRU ordered, most recent at end
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
+}; // class ClientManager
+
+template<class KEY, class VALUE>
+ClientManager<KEY, VALUE>::ClientManager() :
+ ClientManager(DEFAULT_MAX_COST) {}
+
+template<class KEY, class VALUE>
+ClientManager<KEY, VALUE>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {}
+
+template<class KEY, class VALUE>
+ClientManager<KEY, VALUE>::~ClientManager() {}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::wouldEvict(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
+ Mutex::Autolock lock(mLock);
+ return wouldEvictLocked(client);
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE>::getIncompatibleClients(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
+ Mutex::Autolock lock(mLock);
+ return wouldEvictLocked(client, /*returnIncompatibleClients*/true);
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE>::wouldEvictLocked(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
+ bool returnIncompatibleClients) const {
+
+ std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> 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<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::addAndEvict(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& 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<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+ if (curClientPtr->getKey() == (*iter)->getKey()) {
+ iter++;
+ return true;
+ }
+ return false;
+ }), mClients.end());
+
+ mClients.push_back(client);
+
+ return evicted;
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE>::getAll() const {
+ Mutex::Autolock lock(mLock);
+ return mClients;
+}
+
+template<class KEY, class VALUE>
+std::vector<KEY> ClientManager<KEY, VALUE>::getAllKeys() const {
+ Mutex::Autolock lock(mLock);
+ std::vector<KEY> keys(mClients.size());
+ for (const auto& i : mClients) {
+ keys.push_back(i->getKey());
+ }
+ return keys;
+}
+
+template<class KEY, class VALUE>
+std::vector<int32_t> ClientManager<KEY, VALUE>::getAllOwners() const {
+ Mutex::Autolock lock(mLock);
+ std::set<int32_t> owners;
+ for (const auto& i : mClients) {
+ owners.emplace(i->getOwnerId());
+ }
+ return std::vector<int32_t>(owners.begin(), owners.end());
+}
+
+template<class KEY, class VALUE>
+void ClientManager<KEY, VALUE>::updatePriorities(
+ const std::map<int32_t,int32_t>& ownerPriorityList) {
+ Mutex::Autolock lock(mLock);
+ for (auto& i : mClients) {
+ auto j = ownerPriorityList.find(i->getOwnerId());
+ if (j != ownerPriorityList.end()) {
+ i->setPriority(j->second);
+ }
+ }
+}
+
+template<class KEY, class VALUE>
+std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::get(
+ const KEY& key) const {
+ Mutex::Autolock lock(mLock);
+ for (const auto& i : mClients) {
+ if (i->getKey() == key) return i;
+ }
+ return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr);
+}
+
+template<class KEY, class VALUE>
+void ClientManager<KEY, VALUE>::removeAll() {
+ Mutex::Autolock lock(mLock);
+ mClients.clear();
+}
+
+template<class KEY, class VALUE>
+std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::remove(const KEY& key) {
+ Mutex::Autolock lock(mLock);
+
+ std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret;
+
+ // Remove evicted clients from list
+ mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
+ [&key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+ if (curClientPtr->getKey() == key) {
+ ret = curClientPtr;
+ return true;
+ }
+ return false;
+ }), mClients.end());
+
+ return ret;
+}
+
+template<class KEY, class VALUE>
+void ClientManager<KEY, VALUE>::remove(
+ const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) {
+ Mutex::Autolock lock(mLock);
+ // Remove evicted clients from list
+ mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
+ [&value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+ if (curClientPtr == value) {
+ return true;
+ }
+ return false;
+ }), mClients.end());
+}
+
+template<class KEY, class VALUE>
+int64_t ClientManager<KEY, VALUE>::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 <utils/Log.h>
+#include <cutils/compiler.h>
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+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 T>
+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<std::forward_iterator_tag, T> {
+ 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<std::forward_iterator_tag, T> {
+ 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 <class... Args>
+ 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<T> mBuffer;
+}; // class RingBuffer
+
+
+template <class T>
+RingBuffer<T>::RingBuffer(size_t length) : mFrontIdx{0}, mMaxBufferSize{length} {}
+
+template <class T>
+RingBuffer<T>::iterator::iterator(T* ptr, size_t size, size_t pos, size_t ctr) :
+ mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {}
+
+template <class T>
+typename RingBuffer<T>::iterator& RingBuffer<T>::iterator::operator++() {
+ ++mCtr;
+
+ if (CC_UNLIKELY(mCtr == mSize)) {
+ mPos = mSize;
+ return *this;
+ }
+
+ mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1);
+ return *this;
+}
+
+template <class T>
+typename RingBuffer<T>::iterator RingBuffer<T>::iterator::operator++(int) {
+ iterator tmp{mPtr, mSize, mPos, mCtr};
+ ++(*this);
+ return tmp;
+}
+
+template <class T>
+bool RingBuffer<T>::iterator::operator==(const iterator& rhs) {
+ return (mPtr + mPos) == (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+bool RingBuffer<T>::iterator::operator!=(const iterator& rhs) {
+ return (mPtr + mPos) != (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+T& RingBuffer<T>::iterator::operator*() {
+ return *(mPtr + mPos);
+}
+
+template <class T>
+T* RingBuffer<T>::iterator::operator->() {
+ return mPtr + mPos;
+}
+
+template <class T>
+RingBuffer<T>::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 <class T>
+typename RingBuffer<T>::const_iterator& RingBuffer<T>::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 <class T>
+typename RingBuffer<T>::const_iterator RingBuffer<T>::const_iterator::operator++(int) {
+ const_iterator tmp{mPtr, mSize, mPos, mCtr};
+ ++(*this);
+ return tmp;
+}
+
+template <class T>
+bool RingBuffer<T>::const_iterator::operator==(const const_iterator& rhs) {
+ return (mPtr + mPos) == (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+bool RingBuffer<T>::const_iterator::operator!=(const const_iterator& rhs) {
+ return (mPtr + mPos) != (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+const T& RingBuffer<T>::const_iterator::operator*() {
+ return *(mPtr + mPos);
+}
+
+template <class T>
+const T* RingBuffer<T>::const_iterator::operator->() {
+ return mPtr + mPos;
+}
+
+template <class T>
+void RingBuffer<T>::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 <class T>
+void RingBuffer<T>::add(T&& item) {
+ if (mBuffer.size() != mMaxBufferSize) {
+ mBuffer.push_back(std::forward<T>(item));
+ mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+ return;
+ }
+
+ // Only works for types with move assignment operator
+ mBuffer[mFrontIdx] = std::forward<T>(item);
+ mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+}
+
+template <class T>
+template <class... Args>
+void RingBuffer<T>::emplace(Args&&... args) {
+ if (mBuffer.size() != mMaxBufferSize) {
+ mBuffer.emplace_back(std::forward<Args>(args)...);
+ mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+ return;
+ }
+
+ // Only works for types with move assignment operator
+ mBuffer[mFrontIdx] = T(std::forward<Args>(args)...);
+ mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+}
+
+template <class T>
+typename RingBuffer<T>::iterator RingBuffer<T>::begin() {
+ size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1;
+ return iterator(mBuffer.data(), mBuffer.size(), (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0);
+}
+
+template <class T>
+typename RingBuffer<T>::iterator RingBuffer<T>::end() {
+ size_t s = mBuffer.size();
+ return iterator(mBuffer.data(), s, s, s);
+}
+
+template <class T>
+typename RingBuffer<T>::const_iterator RingBuffer<T>::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 <class T>
+typename RingBuffer<T>::const_iterator RingBuffer<T>::end() const {
+ size_t s = mBuffer.size();
+ return const_iterator(mBuffer.data(), s, s, s);
+}
+
+template <class T>
+T& RingBuffer<T>::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 <class T>
+const T& RingBuffer<T>::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 <class T>
+size_t RingBuffer<T>::size() const {
+ return mBuffer.size();
+}
+
+template <class T>
+void RingBuffer<T>::clear() {
+ mBuffer.clear();
+ mFrontIdx = 0;
+}
+
+}; // namespace android
+
+#endif // ANDROID_SERVICE_UTILS_RING_BUFFER_H
+
+