diff options
-rw-r--r-- | camera/Android.mk | 1 | ||||
-rw-r--r-- | camera/CameraBase.cpp | 16 | ||||
-rw-r--r-- | camera/ICameraService.cpp | 33 | ||||
-rw-r--r-- | camera/ICameraServiceListener.cpp | 86 | ||||
-rw-r--r-- | camera/tests/ProCameraTests.cpp | 150 | ||||
-rw-r--r-- | include/camera/CameraBase.h | 6 | ||||
-rw-r--r-- | include/camera/ICameraService.h | 18 | ||||
-rw-r--r-- | include/camera/ICameraServiceListener.h | 64 | ||||
-rw-r--r-- | include/camera/ProCamera.h | 3 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.cpp | 126 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.h | 30 | ||||
-rw-r--r-- | services/camera/libcameraservice/ProCamera2Client.cpp | 68 | ||||
-rw-r--r-- | services/camera/libcameraservice/ProCamera2Client.h | 5 |
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 |