summaryrefslogtreecommitdiffstats
path: root/services/camera/libcameraservice/CameraService.cpp
diff options
context:
space:
mode:
authorIgor Murashkin <iam@google.com>2013-03-20 15:56:31 -0700
committerIgor Murashkin <iam@google.com>2013-03-26 17:16:11 -0700
commitcba2c163555cd329f49d40658ea3ee902e94dda3 (patch)
tree1d125f0012a52db51a1aec733792c1b41959734c /services/camera/libcameraservice/CameraService.cpp
parenta97d15e2e2f0b317a345d3a6f02df80b8988b1f6 (diff)
downloadframeworks_av-cba2c163555cd329f49d40658ea3ee902e94dda3.zip
frameworks_av-cba2c163555cd329f49d40658ea3ee902e94dda3.tar.gz
frameworks_av-cba2c163555cd329f49d40658ea3ee902e94dda3.tar.bz2
Camera: Add hotplug support (for fixed # of cameras)
* Minor: also change addListener to fire the current status upon subscription * Minor: STATUS_AVAILABLE is now an alias for STATUS_PRESENT and deprecated Change-Id: I254608a7332095e3ef201ffea64cff156cfc1b3e
Diffstat (limited to 'services/camera/libcameraservice/CameraService.cpp')
-rw-r--r--services/camera/libcameraservice/CameraService.cpp134
1 files changed, 131 insertions, 3 deletions
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 5a6a3c8..2db5224 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -66,6 +66,20 @@ 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));
+
+ cs->onDeviceStatusChanged(
+ camera_id,
+ new_status);
+}
+} // extern "C"
+
// ----------------------------------------------------------------------------
// This is ugly and only safe if we never re-create the CameraService, but
@@ -79,8 +93,10 @@ CameraService::CameraService()
gCameraService = this;
for (size_t i = 0; i < MAX_CAMERAS; ++i) {
- mStatusList[i] = ICameraServiceListener::STATUS_AVAILABLE;
+ mStatusList[i] = ICameraServiceListener::STATUS_PRESENT;
}
+
+ this->camera_device_status_change = android::camera_device_status_change;
}
void CameraService::onFirstRef()
@@ -105,6 +121,11 @@ void CameraService::onFirstRef()
for (int i = 0; i < mNumberOfCameras; i++) {
setCameraFree(i);
}
+
+ if (mModule->common.module_api_version >=
+ CAMERA_MODULE_API_VERSION_2_1) {
+ mModule->set_callbacks(this);
+ }
}
}
@@ -118,6 +139,67 @@ CameraService::~CameraService() {
gCameraService = NULL;
}
+void CameraService::onDeviceStatusChanged(int cameraId,
+ int newStatus)
+{
+ ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,
+ cameraId, newStatus);
+
+ if (cameraId < 0 || cameraId >= MAX_CAMERAS) {
+ 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);
+ 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;
+ {
+ Mutex::Autolock al(mServiceLock);
+
+ /* Find all clients that we need to disconnect */
+ sp<Client> 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);
+ }
+ }
+ }
+
+ /* now disconnect them. don't hold the lock
+ or we can get into a deadlock */
+
+ for (size_t i = 0; i < clientsToDisconnect.size(); ++i) {
+ sp<BasicClient> client = clientsToDisconnect[i];
+
+ 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
+ */
+ }
+
+ ALOGV("%s: After unplug, disconnected %d clients",
+ __FUNCTION__, clientsToDisconnect.size());
+ }
+
+ updateStatus(
+ static_cast<ICameraServiceListener::Status>(newStatus), cameraId);
+
+}
+
int32_t CameraService::getNumberOfCameras() {
return mNumberOfCameras;
}
@@ -212,6 +294,19 @@ bool CameraService::validateConnect(int cameraId,
return false;
}
+ ICameraServiceListener::Status currentStatus = getStatus(cameraId);
+ if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) {
+ ALOGI("Camera is not plugged in,"
+ " connect X (pid %d) rejected", callingPid);
+ return false;
+ } else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) {
+ ALOGI("Camera is enumerating,"
+ " connect X (pid %d) rejected", callingPid);
+ return false;
+ }
+ // Else don't check for STATUS_NOT_AVAILABLE.
+ // -- It's done implicitly in canConnectUnsafe /w the mBusy array
+
return true;
}
@@ -293,6 +388,7 @@ sp<ICamera> CameraService::connect(
// If there are other non-exclusive users of the camera,
// this will tear them down before we can reuse the camera
if (isValidCameraId(cameraId)) {
+ // transition from PRESENT -> NOT_AVAILABLE
updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE,
cameraId);
}
@@ -321,7 +417,8 @@ sp<ICamera> CameraService::connect(
if (!connectFinishUnsafe(client, client->asBinder())) {
// this is probably not recoverable.. maybe the client can try again
- updateStatus(ICameraServiceListener::STATUS_AVAILABLE, cameraId);
+ // OK: we can only get here if we were originally in PRESENT state
+ updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId);
return NULL;
}
@@ -429,6 +526,15 @@ status_t CameraService::addListener(
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);
+ }
+ }
+
return OK;
}
status_t CameraService::removeListener(
@@ -719,6 +825,8 @@ CameraService::BasicClient::~BasicClient() {
void CameraService::BasicClient::disconnect() {
mCameraService->removeClientByRemote(mRemoteBinder);
+ // client shouldn't be able to call into us anymore
+ mClientPid = 0;
}
status_t CameraService::BasicClient::startCameraOps() {
@@ -816,7 +924,7 @@ void CameraService::Client::notifyError() {
void CameraService::Client::disconnect() {
BasicClient::disconnect();
mCameraService->setCameraFree(mCameraId);
- mCameraService->updateStatus(ICameraServiceListener::STATUS_AVAILABLE,
+ mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT,
mCameraId);
}
@@ -1017,6 +1125,16 @@ void CameraService::updateStatusUnsafe(ICameraServiceListener::Status status,
ALOGV("%s: Status has changed for camera ID %d from 0x%x to 0x%x",
__FUNCTION__, cameraId, (uint32_t)oldStatus, (uint32_t)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__);
+ mStatusList[cameraId] = oldStatus;
+ return;
+ }
+
/**
* ProClients lose their exclusive lock.
* - Done before the CameraClient can initialize the HAL device,
@@ -1041,4 +1159,14 @@ void CameraService::updateStatusUnsafe(ICameraServiceListener::Status status,
}
}
+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];
+}
+
}; // namespace android