diff options
-rw-r--r-- | camera/Camera.cpp | 4 | ||||
-rw-r--r-- | camera/ICameraService.cpp | 11 | ||||
-rw-r--r-- | include/camera/Camera.h | 2 | ||||
-rw-r--r-- | include/camera/ICameraService.h | 2 | ||||
-rwxr-xr-x | media/libstagefright/CameraSource.cpp | 2 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.cpp | 149 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.h | 15 |
7 files changed, 143 insertions, 42 deletions
diff --git a/camera/Camera.cpp b/camera/Camera.cpp index d43cb0b..b81fe86 100644 --- a/camera/Camera.cpp +++ b/camera/Camera.cpp @@ -116,13 +116,13 @@ status_t Camera::getCameraInfo(int cameraId, return cs->getCameraInfo(cameraId, cameraInfo); } -sp<Camera> Camera::connect(int cameraId) +sp<Camera> Camera::connect(int cameraId, bool force, bool keep) { ALOGV("connect"); sp<Camera> c = new Camera(); const sp<ICameraService>& cs = getCameraService(); if (cs != 0) { - c->mCamera = cs->connect(c, cameraId); + c->mCamera = cs->connect(c, cameraId, force, keep); } if (c->mCamera != 0) { c->mCamera->asBinder()->linkToDeath(c); diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp index 85f1a29..c74298a 100644 --- a/camera/ICameraService.cpp +++ b/camera/ICameraService.cpp @@ -56,12 +56,15 @@ public: } // connect to camera service - virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId) + virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId, + bool force, bool keep) { Parcel data, reply; data.writeInterfaceToken(ICameraService::getInterfaceDescriptor()); data.writeStrongBinder(cameraClient->asBinder()); data.writeInt32(cameraId); + data.writeInt32(force); + data.writeInt32(keep); remote()->transact(BnCameraService::CONNECT, data, &reply); return interface_cast<ICamera>(reply.readStrongBinder()); } @@ -93,7 +96,10 @@ status_t BnCameraService::onTransact( case CONNECT: { CHECK_INTERFACE(ICameraService, data, reply); sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder()); - sp<ICamera> camera = connect(cameraClient, data.readInt32()); + const int cameraId = data.readInt32(); + const int force = data.readInt32(); + const int keep = data.readInt32(); + sp<ICamera> camera = connect(cameraClient, cameraId, force, keep); reply->writeStrongBinder(camera->asBinder()); return NO_ERROR; } break; @@ -105,4 +111,3 @@ status_t BnCameraService::onTransact( // ---------------------------------------------------------------------------- }; // namespace android - diff --git a/include/camera/Camera.h b/include/camera/Camera.h index 234e165..3fedea0 100644 --- a/include/camera/Camera.h +++ b/include/camera/Camera.h @@ -72,7 +72,7 @@ public: static int32_t getNumberOfCameras(); static status_t getCameraInfo(int cameraId, struct CameraInfo* cameraInfo); - static sp<Camera> connect(int cameraId); + static sp<Camera> connect(int cameraId, bool force, bool keep); virtual ~Camera(); void init(); diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h index 7d70c1e..97e3169 100644 --- a/include/camera/ICameraService.h +++ b/include/camera/ICameraService.h @@ -42,7 +42,7 @@ public: virtual status_t getCameraInfo(int cameraId, struct CameraInfo* cameraInfo) = 0; virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, - int cameraId) = 0; + int cameraId, bool force, bool keep) = 0; }; // ---------------------------------------------------------------------------- diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 2df5528..0d67800 100755 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -182,7 +182,7 @@ status_t CameraSource::isCameraAvailable( int32_t cameraId) { if (camera == 0) { - mCamera = Camera::connect(cameraId); + mCamera = Camera::connect(cameraId, false, false); if (mCamera == 0) return -EBUSY; mCameraFlags &= ~FLAGS_HOT_CAMERA; } else { diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index adf1d49..22836e3 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -21,6 +21,7 @@ #include <stdio.h> #include <sys/types.h> #include <pthread.h> +#include <time.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -33,6 +34,7 @@ #include <hardware/hardware.h> #include <media/AudioSystem.h> #include <media/mediaplayer.h> +#include <utils/Condition.h> #include <utils/Errors.h> #include <utils/Log.h> #include <utils/String16.h> @@ -42,6 +44,8 @@ namespace android { +#define WAIT_RELEASE_TIMEOUT 250 // 250ms + // ---------------------------------------------------------------------------- // Logging support -- this is for debugging only // Use "adb shell dumpsys media.camera -v 1" to change it. @@ -64,6 +68,13 @@ static int getCallingUid() { return IPCThreadState::self()->getCallingUid(); } +static long long getTimeInMs() { + struct timeval t; + t.tv_sec = t.tv_usec = 0; + gettimeofday(&t, NULL); + return t.tv_sec * 1000LL + t.tv_usec / 1000; +} + // ---------------------------------------------------------------------------- // This is ugly and only safe if we never re-create the CameraService, but @@ -131,7 +142,7 @@ status_t CameraService::getCameraInfo(int cameraId, } sp<ICamera> CameraService::connect( - const sp<ICameraClient>& cameraClient, int cameraId) { + const sp<ICameraClient>& cameraClient, int cameraId, bool force, bool keep) { int callingPid = getCallingPid(); sp<CameraHardwareInterface> hardware = NULL; @@ -157,27 +168,73 @@ sp<ICamera> CameraService::connect( return NULL; } + if (keep && !checkCallingPermission(String16("android.permission.KEEP_CAMERA"))) { + ALOGE("connect X (pid %d) rejected (no KEEP_CAMERA permission).", callingPid); + return NULL; + } + Mutex::Autolock lock(mServiceLock); - if (mClient[cameraId] != 0) { - client = mClient[cameraId].promote(); - if (client != 0) { - if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) { - LOG1("CameraService::connect X (pid %d) (the same client)", - callingPid); - return client; - } else { - ALOGW("CameraService::connect X (pid %d) rejected (existing client).", - callingPid); - return NULL; - } + // Check if there is an existing client. + client = mClient[cameraId].promote(); + if (client != 0 && + cameraClient->asBinder() == client->getCameraClient()->asBinder()) { + LOG1("connect X (pid %d) (the same client)", callingPid); + return client; + } + + if (!force) { + if (mClient[cameraId].promote() != 0) { + ALOGW("connect X (pid %d) rejected (existing client).", callingPid); + return NULL; } mClient[cameraId].clear(); - } + if (mBusy[cameraId]) { + ALOGW("connect X (pid %d) rejected (camera %d is still busy).", + callingPid, cameraId); + return NULL; + } + } else { // force == true + int i = 0; + long long start_time = getTimeInMs(); + while (i < mNumberOfCameras) { + if (getTimeInMs() - start_time >= 3000LL) { + ALOGE("connect X (pid %d) rejected (timeout 3s)", callingPid); + return NULL; + } - if (mBusy[cameraId]) { - ALOGW("CameraService::connect X (pid %d) rejected" - " (camera %d is still busy).", callingPid, cameraId); - return NULL; + client = mClient[i].promote(); + if (client != 0) { + if (client->keep()) { + ALOGW("connect X (pid %d) rejected (existing client wants to keeps the camera)", + callingPid); + return NULL; + } else { + ALOGW("New client (pid %d, id=%d). Disconnect the existing client (id=%d).", + callingPid, cameraId, i); + // Do not hold mServiceLock because disconnect will try to get it. + mServiceLock.unlock(); + client->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0, &i); + client->waitRelease(WAIT_RELEASE_TIMEOUT); + client->disconnectInternal(false); + mServiceLock.lock(); + // Restart from the first client because a new client may have connected + // when mServiceLock is unlocked. + i = 0; + continue; + } + } + + if (mBusy[i]) { + // Give the client a chance to release the hardware. + mServiceLock.unlock(); + usleep(10 * 1000); + mServiceLock.lock(); + i = 0; // Restart from the first client + continue; + } + + i++; + } } struct camera_info info; @@ -195,9 +252,15 @@ sp<ICamera> CameraService::connect( return NULL; } - client = new Client(this, cameraClient, hardware, cameraId, info.facing, callingPid); + client = new Client(this, cameraClient, hardware, cameraId, info.facing, + callingPid, keep); + // We need to clear the hardware here. After the destructor of mServiceLock + // finishes, a new client may connect and disconnect this client. If this + // reference is not cleared, the destructor of CameraHardwareInterface + // cannot run. The new client will not be able to connect. + hardware.clear(); mClient[cameraId] = client; - LOG1("CameraService::connect X"); + LOG1("CameraService::connect X (id %d)", cameraId); return client; } @@ -331,9 +394,9 @@ void CameraService::playSound(sound_kind kind) { CameraService::Client::Client(const sp<CameraService>& cameraService, const sp<ICameraClient>& cameraClient, const sp<CameraHardwareInterface>& hardware, - int cameraId, int cameraFacing, int clientPid) { + int cameraId, int cameraFacing, int clientPid, bool keep) { int callingPid = getCallingPid(); - LOG1("Client::Client E (pid %d)", callingPid); + LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId); mCameraService = cameraService; mCameraClient = cameraClient; @@ -341,6 +404,7 @@ CameraService::Client::Client(const sp<CameraService>& cameraService, mCameraId = cameraId; mCameraFacing = cameraFacing; mClientPid = clientPid; + mKeep = keep; mMsgEnabled = 0; mSurface = 0; mPreviewWindow = 0; @@ -359,7 +423,7 @@ CameraService::Client::Client(const sp<CameraService>& cameraService, mPlayShutterSound = true; cameraService->setCameraBusy(cameraId); cameraService->loadSound(); - LOG1("Client::Client X (pid %d)", callingPid); + LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId); } // tear down the client @@ -468,18 +532,24 @@ static void disconnectWindow(const sp<ANativeWindow>& window) { } void CameraService::Client::disconnect() { + disconnectInternal(true); +} + +void CameraService::Client::disconnectInternal(bool needCheckPid) { int callingPid = getCallingPid(); - LOG1("disconnect E (pid %d)", callingPid); + LOG1("disconnectInternal E (pid %d)", callingPid); Mutex::Autolock lock(mLock); - if (checkPid() != NO_ERROR) { - ALOGW("different client - don't disconnect"); - return; - } + if (needCheckPid) { + if (checkPid() != NO_ERROR) { + ALOGW("different client - don't disconnect"); + return; + } - if (mClientPid <= 0) { - LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid); - return; + if (mClientPid <= 0) { + LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid); + return; + } } // Make sure disconnect() is done once and once only, whether it is called @@ -506,8 +576,16 @@ void CameraService::Client::disconnect() { mCameraService->removeClient(mCameraClient); mCameraService->setCameraFree(mCameraId); + mReleaseCondition.signal(); + + LOG1("disconnectInternal X (pid %d)", callingPid); +} - LOG1("disconnect X (pid %d)", callingPid); +void CameraService::Client::waitRelease(int ms) { + Mutex::Autolock lock(mLock); + if (mHardware != 0) { + mReleaseCondition.waitRelative(mLock, ms * 1000000); + } } // ---------------------------------------------------------------------------- @@ -874,6 +952,9 @@ status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t a return OK; } else if (cmd == CAMERA_CMD_PLAY_RECORDING_SOUND) { mCameraService->playSound(SOUND_RECORDING); + } else if (cmd == CAMERA_CMD_PING) { + // If mHardware is 0, checkPidAndHardware will return error. + return OK; } return mHardware->sendCommand(cmd, arg1, arg2); @@ -1217,6 +1298,10 @@ int CameraService::Client::getOrientation(int degrees, bool mirror) { return -1; } +// Whether the client wants to keep the camera from taking +bool CameraService::Client::keep() const { + return mKeep; +} // ---------------------------------------------------------------------------- diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index bad41f5..457c79b 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -46,7 +46,8 @@ public: virtual int32_t getNumberOfCameras(); virtual status_t getCameraInfo(int cameraId, struct CameraInfo* cameraInfo); - virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId); + virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId, + bool force, bool keep); virtual void removeClient(const sp<ICameraClient>& cameraClient); virtual sp<Client> getClientById(int cameraId); @@ -114,7 +115,8 @@ private: const sp<CameraHardwareInterface>& hardware, int cameraId, int cameraFacing, - int clientPid); + int clientPid, + bool keep); ~Client(); // return our camera client @@ -172,12 +174,19 @@ private: const sp<IBinder>& binder, const sp<ANativeWindow>& window); + void disconnectInternal(bool needCheckPid); + bool keep() const; + void waitRelease(int ms); + + // these are initialized in the constructor. sp<CameraService> mCameraService; // immutable after constructor sp<ICameraClient> mCameraClient; int mCameraId; // immutable after constructor int mCameraFacing; // immutable after constructor pid_t mClientPid; + // Client wants to keep the camera from taking by other clients. + bool mKeep; sp<CameraHardwareInterface> mHardware; // cleared after disconnect() int mPreviewCallbackFlag; int mOrientation; // Current display orientation @@ -185,6 +194,8 @@ private: // Ensures atomicity among the public methods mutable Mutex mLock; + // This will get notified when the hardware is released. + Condition mReleaseCondition; // This is a binder of Surface or SurfaceTexture. sp<IBinder> mSurface; sp<ANativeWindow> mPreviewWindow; |