summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camera/Android.mk1
-rw-r--r--camera/CameraBase.cpp16
-rw-r--r--camera/ICameraService.cpp33
-rw-r--r--camera/ICameraServiceListener.cpp86
-rw-r--r--camera/tests/ProCameraTests.cpp150
-rw-r--r--include/camera/CameraBase.h6
-rw-r--r--include/camera/ICameraService.h18
-rw-r--r--include/camera/ICameraServiceListener.h64
-rw-r--r--include/camera/ProCamera.h3
-rw-r--r--services/camera/libcameraservice/CameraService.cpp126
-rw-r--r--services/camera/libcameraservice/CameraService.h30
-rw-r--r--services/camera/libcameraservice/ProCamera2Client.cpp68
-rw-r--r--services/camera/libcameraservice/ProCamera2Client.h5
13 files changed, 587 insertions, 19 deletions
diff --git a/camera/Android.mk b/camera/Android.mk
index 3f30079..e33fb50 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -11,6 +11,7 @@ LOCAL_SRC_FILES:= \
ICamera.cpp \
ICameraClient.cpp \
ICameraService.cpp \
+ ICameraServiceListener.cpp \
ICameraRecordingProxy.cpp \
ICameraRecordingProxyListener.cpp \
IProCameraUser.cpp \
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index 9b0e6bf..29096da 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -231,6 +231,22 @@ status_t CameraBase<TCam, TCamTraits>::getCameraInfo(int cameraId,
return cs->getCameraInfo(cameraId, cameraInfo);
}
+template <typename TCam, typename TCamTraits>
+status_t CameraBase<TCam, TCamTraits>::addServiceListener(
+ const sp<ICameraServiceListener>& listener) {
+ const sp<ICameraService>& cs = getCameraService();
+ if (cs == 0) return UNKNOWN_ERROR;
+ return cs->addListener(listener);
+}
+
+template <typename TCam, typename TCamTraits>
+status_t CameraBase<TCam, TCamTraits>::removeServiceListener(
+ const sp<ICameraServiceListener>& listener) {
+ const sp<ICameraService>& cs = getCameraService();
+ if (cs == 0) return UNKNOWN_ERROR;
+ return cs->removeListener(listener);
+}
+
template class CameraBase<ProCamera>;
template class CameraBase<Camera>;
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index b54d63f..134f7f0 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -23,6 +23,7 @@
#include <binder/IServiceManager.h>
#include <camera/ICameraService.h>
+#include <camera/ICameraServiceListener.h>
#include <camera/IProCameraUser.h>
#include <camera/IProCameraCallbacks.h>
#include <camera/ICamera.h>
@@ -86,6 +87,24 @@ public:
remote()->transact(BnCameraService::CONNECT_PRO, data, &reply);
return interface_cast<IProCameraUser>(reply.readStrongBinder());
}
+
+ virtual status_t addListener(const sp<ICameraServiceListener>& listener)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ data.writeStrongBinder(listener->asBinder());
+ remote()->transact(BnCameraService::ADD_LISTENER, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t removeListener(const sp<ICameraServiceListener>& listener)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ data.writeStrongBinder(listener->asBinder());
+ remote()->transact(BnCameraService::REMOVE_LISTENER, data, &reply);
+ return reply.readInt32();
+ }
};
IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
@@ -134,6 +153,20 @@ status_t BnCameraService::onTransact(
reply->writeStrongBinder(camera->asBinder());
return NO_ERROR;
} break;
+ case ADD_LISTENER: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ sp<ICameraServiceListener> listener =
+ interface_cast<ICameraServiceListener>(data.readStrongBinder());
+ reply->writeInt32(addListener(listener));
+ return NO_ERROR;
+ } break;
+ case REMOVE_LISTENER: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ sp<ICameraServiceListener> listener =
+ interface_cast<ICameraServiceListener>(data.readStrongBinder());
+ reply->writeInt32(removeListener(listener));
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/camera/ICameraServiceListener.cpp b/camera/ICameraServiceListener.cpp
new file mode 100644
index 0000000..640ee35
--- /dev/null
+++ b/camera/ICameraServiceListener.cpp
@@ -0,0 +1,86 @@
+/*
+**
+** Copyright 2013, 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 <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <camera/ICameraServiceListener.h>
+
+namespace android {
+
+namespace {
+ enum {
+ STATUS_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+ };
+}; // namespace anonymous
+
+class BpCameraServiceListener: public BpInterface<ICameraServiceListener>
+{
+
+public:
+ BpCameraServiceListener(const sp<IBinder>& impl)
+ : BpInterface<ICameraServiceListener>(impl)
+ {
+ }
+
+ virtual void onStatusChanged(Status status, int32_t cameraId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ ICameraServiceListener::getInterfaceDescriptor());
+
+ data.writeInt32(static_cast<int32_t>(status));
+ data.writeInt32(cameraId);
+
+ remote()->transact(STATUS_CHANGED,
+ data,
+ &reply,
+ IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(CameraServiceListener,
+ "android.hardware.ICameraServiceListener");
+
+// ----------------------------------------------------------------------
+
+status_t BnCameraServiceListener::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case STATUS_CHANGED: {
+ CHECK_INTERFACE(ICameraServiceListener, data, reply);
+
+ Status status = static_cast<Status>(data.readInt32());
+ int32_t cameraId = data.readInt32();
+
+ onStatusChanged(status, cameraId);
+
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
index 39456af..c61e71a 100644
--- a/camera/tests/ProCameraTests.cpp
+++ b/camera/tests/ProCameraTests.cpp
@@ -33,6 +33,8 @@
#include <hardware/camera2.h> // for CAMERA2_TEMPLATE_PREVIEW only
#include <camera/CameraMetadata.h>
+#include <camera/ICameraServiceListener.h>
+
namespace android {
namespace camera2 {
namespace tests {
@@ -48,9 +50,9 @@ namespace client {
#define TEST_FORMAT_DEPTH HAL_PIXEL_FORMAT_Y16
// defaults for display "test"
-#define TEST_DISPLAY_FORMAT HAL_PIXEL_FORMAT_Y16
-#define TEST_DISPLAY_WIDTH 1280
-#define TEST_DISPLAY_HEIGHT 960
+#define TEST_DISPLAY_FORMAT HAL_PIXEL_FORMAT_Y8
+#define TEST_DISPLAY_WIDTH 320
+#define TEST_DISPLAY_HEIGHT 240
#define TEST_CPU_FRAME_COUNT 2
#define TEST_CPU_HEAP_COUNT 5
@@ -68,6 +70,52 @@ namespace client {
class ProCameraTest;
+struct ServiceListener : public BnCameraServiceListener {
+
+ ServiceListener() :
+ mLatestStatus(STATUS_UNKNOWN),
+ mPrevStatus(STATUS_UNKNOWN)
+ {
+ }
+
+ void onStatusChanged(Status status, int32_t cameraId) {
+ dout << "On status changed: 0x" << std::hex
+ << status << " cameraId " << cameraId
+ << std::endl;
+
+ Mutex::Autolock al(mMutex);
+
+ mLatestStatus = status;
+ mCondition.broadcast();
+ }
+
+ status_t waitForStatusChange(Status& newStatus) {
+ Mutex::Autolock al(mMutex);
+
+ if (mLatestStatus != mPrevStatus) {
+ newStatus = mLatestStatus;
+ mPrevStatus = mLatestStatus;
+ return OK;
+ }
+
+ status_t stat = mCondition.waitRelative(mMutex,
+ TEST_LISTENER_TIMEOUT);
+
+ if (stat == OK) {
+ newStatus = mLatestStatus;
+ mPrevStatus = mLatestStatus;
+ }
+
+ return stat;
+ }
+
+ Condition mCondition;
+ Mutex mMutex;
+
+ Status mLatestStatus;
+ Status mPrevStatus;
+};
+
enum ProEvent {
UNKNOWN,
ACQUIRED,
@@ -441,7 +489,6 @@ protected:
}
request.acquire(requestTmp);
}
-
};
sp<Thread> ProCameraTest::mTestThread;
@@ -538,18 +585,52 @@ TEST_F(ProCameraTest, DISABLED_StreamingImageSingle) {
}
int depthStreamId = -1;
- EXPECT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt, surface,
- &depthStreamId));
- EXPECT_NE(-1, depthStreamId);
- EXPECT_OK(mCamera->exclusiveTryLock());
+ sp<ServiceListener> listener = new ServiceListener();
+ EXPECT_OK(ProCamera::addServiceListener(listener));
- uint8_t streams[] = { depthStreamId };
- ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1));
+ ServiceListener::Status currentStatus = ServiceListener::STATUS_AVAILABLE;
- dout << "will sleep now for " << mDisplaySecs << std::endl;
- sleep(mDisplaySecs);
+ dout << "Will now stream and resume infinitely..." << std::endl;
+ while (true) {
+
+ if (currentStatus == ServiceListener::STATUS_AVAILABLE) {
+
+ EXPECT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt,
+ surface,
+ &depthStreamId));
+ EXPECT_NE(-1, depthStreamId);
+ EXPECT_OK(mCamera->exclusiveTryLock());
+
+ uint8_t streams[] = { depthStreamId };
+ ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(
+ streams,
+ /*count*/1));
+ }
+
+ ServiceListener::Status stat = ServiceListener::STATUS_UNKNOWN;
+
+ // TODO: maybe check for getch every once in a while?
+ while (listener->waitForStatusChange(/*out*/stat) != OK);
+
+ if (currentStatus != stat) {
+ if (stat == ServiceListener::STATUS_AVAILABLE) {
+ 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 {
+ dout << "Unknown status change "
+ << std::hex << stat << std::endl;
+ }
+
+ currentStatus = stat;
+ }
+ }
+
+ EXPECT_OK(ProCamera::removeServiceListener(listener));
EXPECT_OK(mCamera->deleteStream(depthStreamId));
EXPECT_OK(mCamera->exclusiveUnlock());
}
@@ -1035,6 +1116,51 @@ TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFrames) {
+//TODO: refactor into separate file
+TEST_F(ProCameraTest, ServiceListenersSubscribe) {
+
+ ASSERT_EQ(4u, sizeof(ServiceListener::Status));
+
+ sp<ServiceListener> listener = new ServiceListener();
+
+ EXPECT_EQ(BAD_VALUE, ProCamera::removeServiceListener(listener));
+ EXPECT_OK(ProCamera::addServiceListener(listener));
+
+ EXPECT_EQ(ALREADY_EXISTS, ProCamera::addServiceListener(listener));
+ EXPECT_OK(ProCamera::removeServiceListener(listener));
+
+ EXPECT_EQ(BAD_VALUE, ProCamera::removeServiceListener(listener));
+}
+
+//TODO: refactor into separate file
+TEST_F(ProCameraTest, ServiceListenersFunctional) {
+
+ sp<ServiceListener> listener = new ServiceListener();
+
+ EXPECT_OK(ProCamera::addServiceListener(listener));
+
+ sp<Camera> cam = Camera::connect(CAMERA_ID,
+ /*clientPackageName*/String16(),
+ -1);
+ EXPECT_NE((void*)NULL, cam.get());
+
+ ServiceListener::Status stat = ServiceListener::STATUS_UNKNOWN;
+ EXPECT_OK(listener->waitForStatusChange(/*out*/stat));
+
+ EXPECT_EQ(ServiceListener::STATUS_NOT_AVAILABLE, stat);
+
+ if (cam.get()) {
+ cam->disconnect();
+ }
+
+ EXPECT_OK(listener->waitForStatusChange(/*out*/stat));
+ EXPECT_EQ(ServiceListener::STATUS_AVAILABLE, stat);
+
+ EXPECT_OK(ProCamera::removeServiceListener(listener));
+}
+
+
+
}
}
}
diff --git a/include/camera/CameraBase.h b/include/camera/CameraBase.h
index fed28ea..2735a86 100644
--- a/include/camera/CameraBase.h
+++ b/include/camera/CameraBase.h
@@ -71,6 +71,12 @@ public:
/*out*/
struct CameraInfo* cameraInfo);
+ static status_t addServiceListener(
+ const sp<ICameraServiceListener>& listener);
+
+ static status_t removeServiceListener(
+ const sp<ICameraServiceListener>& listener);
+
sp<TCamUser> remote();
// Status is set to 'UNKNOWN_ERROR' after successful (re)connection
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index ef2b685..aaf6eb3 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -27,6 +27,7 @@ class ICamera;
class ICameraClient;
class IProCameraUser;
class IProCameraCallbacks;
+class ICameraServiceListener;
class ICameraService : public IInterface
{
@@ -35,7 +36,9 @@ public:
GET_NUMBER_OF_CAMERAS = IBinder::FIRST_CALL_TRANSACTION,
GET_CAMERA_INFO,
CONNECT,
- CONNECT_PRO
+ CONNECT_PRO,
+ ADD_LISTENER,
+ REMOVE_LISTENER,
};
enum {
@@ -45,9 +48,18 @@ public:
public:
DECLARE_META_INTERFACE(CameraService);
- virtual int32_t getNumberOfCameras() = 0;
- virtual status_t getCameraInfo(int cameraId,
+ virtual int32_t getNumberOfCameras() = 0;
+ virtual status_t getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo) = 0;
+
+ // Returns 'OK' if operation succeeded
+ // - Errors: ALREADY_EXISTS if the listener was already added
+ virtual status_t addListener(const sp<ICameraServiceListener>& listener)
+ = 0;
+ // Returns 'OK' if operation succeeded
+ // - Errors: BAD_VALUE if specified listener was not in the listener list
+ virtual status_t removeListener(const sp<ICameraServiceListener>& listener)
+ = 0;
/**
* clientPackageName and clientUid are used for permissions checking. if
* clientUid == USE_CALLING_UID, then the calling UID is used instead. Only
diff --git a/include/camera/ICameraServiceListener.h b/include/camera/ICameraServiceListener.h
new file mode 100644
index 0000000..207116a
--- /dev/null
+++ b/include/camera/ICameraServiceListener.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 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_HARDWARE_ICAMERASERVICE_LISTENER_H
+#define ANDROID_HARDWARE_ICAMERASERVICE_LISTENER_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <hardware/camera_common.h>
+
+namespace android {
+
+class ICameraServiceListener : public IInterface
+{
+public:
+
+ enum Status {
+ // Device physically unplugged
+ STATUS_PRESENT = CAMERA_DEVICE_STATUS_PRESENT,
+ // Device physically re-plugged
+ STATUS_NOT_PRESENT = CAMERA_DEVICE_STATUS_NOT_PRESENT,
+
+ // Camera can be used exclusively
+ STATUS_AVAILABLE = 0x80000000,
+ // Camera is in use by another app and cannot be used exclusively
+ STATUS_NOT_AVAILABLE,
+
+ // Use to initialize variables only
+ STATUS_UNKNOWN = 0xFFFFFFFF,
+ };
+
+ DECLARE_META_INTERFACE(CameraServiceListener);
+
+ virtual void onStatusChanged(Status status, int32_t cameraId) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnCameraServiceListener : public BnInterface<ICameraServiceListener>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif
diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h
index b228145..e8dcdef 100644
--- a/include/camera/ProCamera.h
+++ b/include/camera/ProCamera.h
@@ -169,6 +169,9 @@ public:
/**
* Delete a stream.
* Lock free.
+ *
+ * NOTE: As a side effect this cancels ALL streaming requests.
+ *
* Errors: BAD_VALUE if unknown stream ID.
* PERMISSION_DENIED if the stream wasn't yours
*/
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index d7c8807..8c4f619 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -77,6 +77,10 @@ CameraService::CameraService()
{
ALOGI("CameraService started (pid=%d)", getpid());
gCameraService = this;
+
+ for (size_t i = 0; i < MAX_CAMERAS; ++i) {
+ mStatusList[i] = ICameraServiceListener::STATUS_AVAILABLE;
+ }
}
void CameraService::onFirstRef()
@@ -155,6 +159,23 @@ int CameraService::getDeviceVersion(int cameraId, int* facing) {
return deviceVersion;
}
+bool CameraService::isValidCameraId(int cameraId) {
+ int facing;
+ int deviceVersion = getDeviceVersion(cameraId, &facing);
+
+ switch(deviceVersion) {
+ case CAMERA_DEVICE_API_VERSION_1_0:
+ case CAMERA_DEVICE_API_VERSION_2_0:
+ case CAMERA_DEVICE_API_VERSION_2_1:
+ case CAMERA_DEVICE_API_VERSION_3_0:
+ return true;
+ default:
+ return false;
+ }
+
+ return false;
+}
+
sp<ICamera> CameraService::connect(
const sp<ICameraClient>& cameraClient,
int cameraId,
@@ -236,6 +257,10 @@ sp<ICamera> CameraService::connect(
int facing = -1;
int deviceVersion = getDeviceVersion(cameraId, &facing);
+ if (isValidCameraId(cameraId)) {
+ updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, cameraId);
+ }
+
switch(deviceVersion) {
case CAMERA_DEVICE_API_VERSION_1_0:
client = new CameraClient(this, cameraClient,
@@ -259,6 +284,9 @@ sp<ICamera> CameraService::connect(
}
if (client->initialize(mModule) != OK) {
+ // this is probably not recoverable.. but maybe the client can try again
+ updateStatus(ICameraServiceListener::STATUS_AVAILABLE, cameraId);
+
return NULL;
}
@@ -266,6 +294,7 @@ sp<ICamera> CameraService::connect(
mClient[cameraId] = client;
LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid());
+
return client;
}
@@ -275,6 +304,7 @@ sp<IProCameraUser> CameraService::connect(
const String16& clientPackageName,
int clientUid)
{
+ String8 clientName8(clientPackageName);
int callingPid = getCallingPid();
// TODO: use clientPackageName and clientUid with appOpsMangr
@@ -301,6 +331,15 @@ sp<IProCameraUser> CameraService::connect(
return NULL;
}
+ // TODO: allow concurrent connections with a ProCamera
+ if (mBusy[cameraId]) {
+
+ ALOGW("CameraService::connectPro X (pid %d, \"%s\") rejected"
+ " (camera %d is still busy).", callingPid,
+ clientName8.string(), cameraId);
+ return NULL;
+ }
+
int facing = -1;
int deviceVersion = getDeviceVersion(cameraId, &facing);
@@ -333,9 +372,45 @@ sp<IProCameraUser> CameraService::connect(
LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId,
getpid());
return client;
+}
+status_t CameraService::addListener(
+ const sp<ICameraServiceListener>& listener) {
+ ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
- return NULL;
+ Mutex::Autolock lock(mServiceLock);
+
+ Vector<sp<ICameraServiceListener> >::iterator it, end;
+ for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
+ if ((*it)->asBinder() == listener->asBinder()) {
+ ALOGW("%s: Tried to add listener %p which was already subscribed",
+ __FUNCTION__, listener.get());
+ return ALREADY_EXISTS;
+ }
+ }
+
+ mListenerList.push_back(listener);
+
+ return OK;
+}
+status_t CameraService::removeListener(
+ const sp<ICameraServiceListener>& listener) {
+ ALOGV("%s: Remove listener %p", __FUNCTION__, listener.get());
+
+ Mutex::Autolock lock(mServiceLock);
+
+ Vector<sp<ICameraServiceListener> >::iterator it;
+ for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
+ if ((*it)->asBinder() == listener->asBinder()) {
+ mListenerList.erase(it);
+ return OK;
+ }
+ }
+
+ ALOGW("%s: Tried to remove a listener %p which was not subscribed",
+ __FUNCTION__, listener.get());
+
+ return BAD_VALUE;
}
void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) {
@@ -699,6 +774,8 @@ void CameraService::Client::notifyError() {
void CameraService::Client::disconnect() {
BasicClient::disconnect();
mCameraService->setCameraFree(mCameraId);
+ mCameraService->updateStatus(ICameraServiceListener::STATUS_AVAILABLE,
+ mCameraId);
}
CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client):
@@ -774,6 +851,10 @@ bool CameraService::ProClient::hasExclusiveLock() {
return false;
}
+void CameraService::ProClient::onExclusiveLockStolen() {
+ ALOGE("%s: not implemented yet", __FUNCTION__);
+}
+
status_t CameraService::ProClient::submitRequest(camera_metadata_t* request, bool streaming) {
ALOGE("%s: not implemented yet", __FUNCTION__);
@@ -944,4 +1025,47 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) {
}
+void CameraService::updateStatus(ICameraServiceListener::Status status,
+ int32_t cameraId) {
+ // do not lock mServiceLock here or can get into a deadlock from
+ // connect() -> ProClient::disconnect -> updateStatus
+ Mutex::Autolock lock(mStatusMutex);
+ updateStatusUnsafe(status, cameraId);
+}
+
+void CameraService::updateStatusUnsafe(ICameraServiceListener::Status status,
+ int32_t cameraId) {
+
+ 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);
+
+ /**
+ * 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();
+ }
+ }
+ }
+
+ Vector<sp<ICameraServiceListener> >::const_iterator it;
+ for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
+ (*it)->onStatusChanged(status, cameraId);
+ }
+ }
+}
+
}; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index d93aa73..8acc63f 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -30,6 +30,8 @@
#include <camera/IProCameraUser.h>
#include <camera/IProCameraCallbacks.h>
+#include <camera/ICameraServiceListener.h>
+
/* This needs to be increased if we can have more cameras */
#define MAX_CAMERAS 2
@@ -67,6 +69,10 @@ public:
virtual sp<IProCameraUser> connect(const sp<IProCameraCallbacks>& cameraCb,
int cameraId, const String16& clientPackageName, int clientUid);
+ virtual status_t addListener(const sp<ICameraServiceListener>& listener);
+ virtual status_t removeListener(
+ const sp<ICameraServiceListener>& listener);
+
// Extra permissions checks
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
@@ -263,6 +269,9 @@ public:
virtual status_t requestStream(int streamId);
virtual status_t cancelStream(int streamId);
+ // Callbacks from camera service
+ virtual void onExclusiveLockStolen();
+
protected:
virtual void notifyError();
@@ -303,11 +312,30 @@ private:
camera_module_t *mModule;
+ Vector<sp<ICameraServiceListener> >
+ mListenerList;
+
+ // guard only mStatusList and the broadcasting of ICameraServiceListener
+ Mutex mStatusMutex;
+ ICameraServiceListener::Status
+ mStatusList[MAX_CAMERAS];
+
+ // Broadcast the new status if it changed (locks the service mutex)
+ void updateStatus(
+ ICameraServiceListener::Status status,
+ int32_t cameraId);
+ // Call this one when the service mutex is already held (idempotent)
+ void updateStatusUnsafe(
+ ICameraServiceListener::Status status,
+ int32_t cameraId);
+
// IBinder::DeathRecipient implementation
- virtual void binderDied(const wp<IBinder> &who);
+ virtual void binderDied(const wp<IBinder> &who);
// Helpers
int getDeviceVersion(int cameraId, int* facing);
+
+ bool isValidCameraId(int cameraId);
};
} // namespace android
diff --git a/services/camera/libcameraservice/ProCamera2Client.cpp b/services/camera/libcameraservice/ProCamera2Client.cpp
index eda3012..6fed8b4 100644
--- a/services/camera/libcameraservice/ProCamera2Client.cpp
+++ b/services/camera/libcameraservice/ProCamera2Client.cpp
@@ -115,6 +115,8 @@ status_t ProCamera2Client::exclusiveTryLock() {
Mutex::Autolock icl(mIProCameraUserLock);
SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+ if (!mDevice.get()) return PERMISSION_DENIED;
+
if (!mExclusiveLock) {
mExclusiveLock = true;
@@ -144,6 +146,8 @@ status_t ProCamera2Client::exclusiveLock() {
Mutex::Autolock icl(mIProCameraUserLock);
SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+ if (!mDevice.get()) return PERMISSION_DENIED;
+
/**
* TODO: this should asynchronously 'wait' until the lock becomes available
* if another client already has an exclusive lock.
@@ -197,12 +201,33 @@ bool ProCamera2Client::hasExclusiveLock() {
return mExclusiveLock;
}
+void ProCamera2Client::onExclusiveLockStolen() {
+ ALOGV("%s: ProClient lost exclusivity (id %d)",
+ __FUNCTION__, mCameraId);
+
+ Mutex::Autolock icl(mIProCameraUserLock);
+ SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+ if (mExclusiveLock && mRemoteCallback.get() != NULL) {
+ mRemoteCallback->onLockStatusChanged(
+ IProCameraCallbacks::LOCK_STOLEN);
+ }
+
+ mExclusiveLock = false;
+
+ //TODO: we should not need to detach the device, merely reset it.
+ detachDevice();
+}
+
status_t ProCamera2Client::submitRequest(camera_metadata_t* request,
bool streaming) {
ATRACE_CALL();
ALOGV("%s", __FUNCTION__);
Mutex::Autolock icl(mIProCameraUserLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
if (!mExclusiveLock) {
return PERMISSION_DENIED;
}
@@ -224,6 +249,9 @@ status_t ProCamera2Client::cancelRequest(int requestId) {
ALOGV("%s", __FUNCTION__);
Mutex::Autolock icl(mIProCameraUserLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
if (!mExclusiveLock) {
return PERMISSION_DENIED;
}
@@ -247,6 +275,7 @@ status_t ProCamera2Client::cancelStream(int streamId) {
Mutex::Autolock icl(mIProCameraUserLock);
+ if (!mDevice.get()) return DEAD_OBJECT;
mDevice->clearStreamingRequest();
status_t code;
@@ -274,6 +303,8 @@ status_t ProCamera2Client::createStream(int width, int height, int format,
Mutex::Autolock icl(mIProCameraUserLock);
+ if (!mDevice.get()) return DEAD_OBJECT;
+
sp<IBinder> binder;
sp<ANativeWindow> window;
if (bufferProducer != 0) {
@@ -303,6 +334,8 @@ status_t ProCamera2Client::createDefaultRequest(int templateId,
Mutex::Autolock icl(mIProCameraUserLock);
+ if (!mDevice.get()) return DEAD_OBJECT;
+
CameraMetadata metadata;
if ( (res = mDevice->createDefaultRequest(templateId, &metadata) ) == OK) {
*request = metadata.release();
@@ -319,6 +352,10 @@ status_t ProCamera2Client::getCameraInfo(int cameraId,
return INVALID_OPERATION;
}
+ Mutex::Autolock icl(mIProCameraUserLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
CameraMetadata deviceInfo = mDevice->info();
*info = deviceInfo.release();
@@ -341,6 +378,12 @@ status_t ProCamera2Client::dump(int fd, const Vector<String16>& args) {
result = " Device dump:\n";
write(fd, result.string(), result.size());
+ if (!mDevice.get()) {
+ result = " *** Device is detached\n";
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+ }
+
status_t res = mDevice->dump(fd, args);
if (res != OK) {
result = String8::format(" Error dumping device: %s (%d)",
@@ -363,9 +406,19 @@ void ProCamera2Client::disconnect() {
int callingPid = getCallingPid();
if (callingPid != mClientPid && callingPid != mServicePid) return;
+ ALOGV("Camera %d: Shutting down", mCameraId);
+
+ detachDevice();
+ ProClient::disconnect();
+
+ ALOGV("Camera %d: Shut down complete complete", mCameraId);
+}
+
+void ProCamera2Client::detachDevice() {
if (mDevice == 0) return;
- ALOGV("Camera %d: Shutting down", mCameraId);
+ ALOGV("Camera %d: Stopping processors", mCameraId);
+
mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
FRAME_PROCESSOR_LISTENER_MAX_ID,
/*listener*/this);
@@ -374,11 +427,22 @@ void ProCamera2Client::disconnect() {
mFrameProcessor->join();
ALOGV("Camera %d: Disconnecting device", mCameraId);
+ // WORKAROUND: HAL refuses to disconnect while there's streams in flight
+ {
+ mDevice->clearStreamingRequest();
+
+ status_t code;
+ if ((code = mDevice->waitUntilDrained()) != OK) {
+ ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__,
+ code);
+ }
+ }
+
mDevice->disconnect();
mDevice.clear();
- ProClient::disconnect();
+ ALOGV("Camera %d: Detach complete", mCameraId);
}
status_t ProCamera2Client::connect(const sp<IProCameraCallbacks>& client) {
diff --git a/services/camera/libcameraservice/ProCamera2Client.h b/services/camera/libcameraservice/ProCamera2Client.h
index 9f514f4..ff6f4e2 100644
--- a/services/camera/libcameraservice/ProCamera2Client.h
+++ b/services/camera/libcameraservice/ProCamera2Client.h
@@ -104,6 +104,9 @@ public:
const sp<Camera2Device>& getCameraDevice();
const sp<CameraService>& getCameraService();
+ // Callbacks from camera service
+ virtual void onExclusiveLockStolen();
+
/**
* Interface used by independent components of ProCamera2Client.
*/
@@ -167,6 +170,8 @@ private:
// - if no we can't modify the request queue.
// note that creating/deleting streams we own is still OK
bool mExclusiveLock;
+
+ void detachDevice();
};
}; // namespace android