summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camera/tests/ProCameraTests.cpp18
-rw-r--r--include/camera/ICameraServiceListener.h11
-rw-r--r--services/camera/libcameraservice/CameraService.cpp134
-rw-r--r--services/camera/libcameraservice/CameraService.h14
4 files changed, 162 insertions, 15 deletions
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
index ecc0854..5f8f772 100644
--- a/camera/tests/ProCameraTests.cpp
+++ b/camera/tests/ProCameraTests.cpp
@@ -587,14 +587,19 @@ TEST_F(ProCameraTest, DISABLED_StreamingImageSingle) {
sp<ServiceListener> listener = new ServiceListener();
EXPECT_OK(ProCamera::addServiceListener(listener));
- ServiceListener::Status currentStatus = ServiceListener::STATUS_AVAILABLE;
+ ServiceListener::Status currentStatus;
+
+ // when subscribing a new listener,
+ // we immediately get a callback to the current status
+ while (listener->waitForStatusChange(/*out*/currentStatus) != OK);
+ EXPECT_EQ(ServiceListener::STATUS_PRESENT, currentStatus);
dout << "Will now stream and resume infinitely..." << std::endl;
while (true) {
- if (currentStatus == ServiceListener::STATUS_AVAILABLE) {
+ if (currentStatus == ServiceListener::STATUS_PRESENT) {
- EXPECT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt,
+ ASSERT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt,
surface,
&depthStreamId));
EXPECT_NE(-1, depthStreamId);
@@ -613,12 +618,15 @@ TEST_F(ProCameraTest, DISABLED_StreamingImageSingle) {
while (listener->waitForStatusChange(/*out*/stat) != OK);
if (currentStatus != stat) {
- if (stat == ServiceListener::STATUS_AVAILABLE) {
+ if (stat == ServiceListener::STATUS_PRESENT) {
dout << "Reconnecting to camera" << std::endl;
mCamera = ProCamera::connect(CAMERA_ID);
} else if (stat == ServiceListener::STATUS_NOT_AVAILABLE) {
dout << "Disconnecting from camera" << std::endl;
mCamera->disconnect();
+ } else if (stat == ServiceListener::STATUS_NOT_PRESENT) {
+ dout << "Camera unplugged" << std::endl;
+ mCamera = NULL;
} else {
dout << "Unknown status change "
<< std::hex << stat << std::endl;
@@ -1216,7 +1224,7 @@ TEST_F(ProCameraTest, ServiceListenersFunctional) {
}
EXPECT_OK(listener->waitForStatusChange(/*out*/stat));
- EXPECT_EQ(ServiceListener::STATUS_AVAILABLE, stat);
+ EXPECT_EQ(ServiceListener::STATUS_PRESENT, stat);
EXPECT_OK(ProCamera::removeServiceListener(listener));
}
diff --git a/include/camera/ICameraServiceListener.h b/include/camera/ICameraServiceListener.h
index 88860dd..f2a11c2 100644
--- a/include/camera/ICameraServiceListener.h
+++ b/include/camera/ICameraServiceListener.h
@@ -38,9 +38,8 @@ public:
* NOT_PRESENT -> PRESENT
* NOT_PRESENT -> ENUMERATING
* ENUMERATING -> PRESENT
- * PRESENT -> AVAILABLE
- * AVAILABLE -> NOT_AVAILABLE
- * NOT_AVAILABLE -> AVAILABLE
+ * PRESENT -> NOT_AVAILABLE
+ * NOT_AVAILABLE -> PRESENT
*
* A state will never immediately transition back to itself.
*/
@@ -48,15 +47,17 @@ public:
// Device physically unplugged
STATUS_NOT_PRESENT = CAMERA_DEVICE_STATUS_NOT_PRESENT,
// Device physically has been plugged in
+ // and the camera can be used exlusively
STATUS_PRESENT = CAMERA_DEVICE_STATUS_PRESENT,
// Device physically has been plugged in
// but it will not be connect-able until enumeration is complete
STATUS_ENUMERATING = CAMERA_DEVICE_STATUS_ENUMERATING,
// Camera can be used exclusively
- STATUS_AVAILABLE = 0x80000000,
+ STATUS_AVAILABLE = STATUS_PRESENT, // deprecated, will be removed
+
// Camera is in use by another app and cannot be used exclusively
- STATUS_NOT_AVAILABLE,
+ STATUS_NOT_AVAILABLE = 0x80000000,
// Use to initialize variables only
STATUS_UNKNOWN = 0xFFFFFFFF,
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
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index c5e495f..8cb1691 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -45,7 +45,8 @@ class MediaPlayer;
class CameraService :
public BinderService<CameraService>,
public BnCameraService,
- public IBinder::DeathRecipient
+ public IBinder::DeathRecipient,
+ public camera_module_callbacks_t
{
friend class BinderService<CameraService>;
public:
@@ -59,6 +60,11 @@ public:
virtual ~CameraService();
/////////////////////////////////////////////////////////////////////
+ // HAL Callbacks
+ virtual void onDeviceStatusChanged(int cameraId,
+ int newStatus);
+
+ /////////////////////////////////////////////////////////////////////
// ICameraService
virtual int32_t getNumberOfCameras();
virtual status_t getCameraInfo(int cameraId,
@@ -327,10 +333,14 @@ private:
mListenerList;
// guard only mStatusList and the broadcasting of ICameraServiceListener
- Mutex mStatusMutex;
+ mutable Mutex mStatusMutex;
ICameraServiceListener::Status
mStatusList[MAX_CAMERAS];
+ // Read the current status (locks mStatusMutex)
+ ICameraServiceListener::Status
+ getStatus(int cameraId) const;
+
// Broadcast the new status if it changed (locks the service mutex)
void updateStatus(
ICameraServiceListener::Status status,