diff options
author | Chih-Chung Chang <chihchung@google.com> | 2010-05-06 16:36:58 +0800 |
---|---|---|
committer | Chih-Chung Chang <chihchung@google.com> | 2010-05-10 11:21:14 +0800 |
commit | 5997faf9318df56c4a2644709483ff1f6c0fb14d (patch) | |
tree | 50064747f331ba344a54744741edd050675aa525 | |
parent | 81b3d23bd07b9c955be9c0b1933912eca22a1327 (diff) | |
download | frameworks_native-5997faf9318df56c4a2644709483ff1f6c0fb14d.zip frameworks_native-5997faf9318df56c4a2644709483ff1f6c0fb14d.tar.gz frameworks_native-5997faf9318df56c4a2644709483ff1f6c0fb14d.tar.bz2 |
Support multiple cameras in framework.
Change-Id: I081f0fbdca4b633715ea7c3b3d42f8662d27598a
-rw-r--r-- | camera/libcameraservice/Android.mk | 35 | ||||
-rw-r--r-- | camera/libcameraservice/CameraHardwareStub.cpp | 45 | ||||
-rw-r--r-- | camera/libcameraservice/CameraHardwareStub.h | 4 | ||||
-rw-r--r-- | camera/libcameraservice/CameraService.cpp | 1780 | ||||
-rw-r--r-- | camera/libcameraservice/CameraService.h | 254 | ||||
-rw-r--r-- | camera/libcameraservice/FakeCamera.cpp | 25 | ||||
-rw-r--r-- | camera/libcameraservice/FakeCamera.h | 2 | ||||
-rw-r--r-- | camera/tests/CameraServiceTest/CameraServiceTest.cpp | 212 |
8 files changed, 1128 insertions, 1229 deletions
diff --git a/camera/libcameraservice/Android.mk b/camera/libcameraservice/Android.mk index df5c166..a9489ac 100644 --- a/camera/libcameraservice/Android.mk +++ b/camera/libcameraservice/Android.mk @@ -1,15 +1,24 @@ LOCAL_PATH:= $(call my-dir) -# -# Set USE_CAMERA_STUB for non-emulator and non-simulator builds, if you want -# the camera service to use the fake camera. For emulator or simulator builds, -# we always use the fake camera. +# Set USE_CAMERA_STUB if you want to use the fake camera. +# Set USE_CAMERA_HARDWARE if you want to use the hardware camera. +# For emulator or simulator builds, we use the fake camera only by default. -ifeq ($(USE_CAMERA_STUB),) -USE_CAMERA_STUB:=false ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),) -USE_CAMERA_STUB:=true -endif #libcamerastub + ifeq ($(USE_CAMERA_STUB),) + USE_CAMERA_STUB:=true + endif + ifeq ($(USE_CAMERA_HARDWARE),) + USE_CAMERA_HARDWARE:=false + endif +else +# force USE_CAMERA_STUB for testing temporarily +# ifeq ($(USE_CAMERA_STUB),) + USE_CAMERA_STUB:=true +# endif + ifeq ($(USE_CAMERA_HARDWARE),) + USE_CAMERA_HARDWARE:=true + endif endif ifeq ($(USE_CAMERA_STUB),true) @@ -54,18 +63,18 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_MODULE:= libcameraservice -LOCAL_CFLAGS += -DLOG_TAG=\"CameraService\" - ifeq ($(TARGET_SIMULATOR),true) LOCAL_CFLAGS += -DSINGLE_PROCESS endif ifeq ($(USE_CAMERA_STUB), true) LOCAL_STATIC_LIBRARIES += libcamerastub -LOCAL_CFLAGS += -include CameraHardwareStub.h -else +LOCAL_CFLAGS += -DUSE_CAMERA_STUB +endif + +ifeq ($(USE_CAMERA_HARDWARE),true) +LOCAL_CFLAGS += -DUSE_CAMERA_HARDWARE LOCAL_SHARED_LIBRARIES += libcamera endif include $(BUILD_SHARED_LIBRARY) - diff --git a/camera/libcameraservice/CameraHardwareStub.cpp b/camera/libcameraservice/CameraHardwareStub.cpp index 8b66389..fda48e8 100644 --- a/camera/libcameraservice/CameraHardwareStub.cpp +++ b/camera/libcameraservice/CameraHardwareStub.cpp @@ -47,14 +47,14 @@ void CameraHardwareStub::initDefaultParameters() { CameraParameters p; - p.set("preview-size-values","320x240"); + p.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240"); p.setPreviewSize(320, 240); p.setPreviewFrameRate(15); - p.setPreviewFormat("yuv422sp"); + p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP); - p.set("picture-size-values", "320x240"); + p.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "320x240"); p.setPictureSize(320, 240); - p.setPictureFormat("jpeg"); + p.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG); if (setParameters(p) != NO_ERROR) { LOGE("Failed to set default parameters?!"); @@ -66,14 +66,14 @@ void CameraHardwareStub::initHeapLocked() // Create raw heap. int picture_width, picture_height; mParameters.getPictureSize(&picture_width, &picture_height); - mRawHeap = new MemoryHeapBase(picture_width * 2 * picture_height); + mRawHeap = new MemoryHeapBase(picture_width * picture_height * 3 / 2); int preview_width, preview_height; mParameters.getPreviewSize(&preview_width, &preview_height); LOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height); - // Note that we enforce yuv422 in setParameters(). - int how_big = preview_width * preview_height * 2; + // Note that we enforce yuv420sp in setParameters(). + int how_big = preview_width * preview_height * 3 / 2; // If we are being reinitialized to the same size as before, no // work needs to be done. @@ -99,7 +99,6 @@ CameraHardwareStub::~CameraHardwareStub() { delete mFakeCamera; mFakeCamera = 0; // paranoia - singleton.clear(); } sp<IMemoryHeap> CameraHardwareStub::getPreviewHeap() const @@ -175,7 +174,7 @@ int CameraHardwareStub::previewThread() // Fill the current frame with the fake camera. uint8_t *frame = ((uint8_t *)base) + offset; - fakeCamera->getNextFrameAsYuv422(frame); + fakeCamera->getNextFrameAsYuv420(frame); //LOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame); @@ -288,9 +287,9 @@ int CameraHardwareStub::pictureThread() // In the meantime just make another fake camera picture. int w, h; mParameters.getPictureSize(&w, &h); - sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * 2 * h); + sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * h * 3 / 2); FakeCamera cam(w, h); - cam.getNextFrameAsYuv422((uint8_t *)mRawHeap->base()); + cam.getNextFrameAsYuv420((uint8_t *)mRawHeap->base()); mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie); } @@ -307,7 +306,7 @@ status_t CameraHardwareStub::takePicture() { stopPreview(); if (createThread(beginPictureThread, this) == false) - return -1; + return UNKNOWN_ERROR; return NO_ERROR; } @@ -339,12 +338,14 @@ status_t CameraHardwareStub::setParameters(const CameraParameters& params) Mutex::Autolock lock(mLock); // XXX verify params - if (strcmp(params.getPreviewFormat(), "yuv422sp") != 0) { - LOGE("Only yuv422sp preview is supported"); + if (strcmp(params.getPreviewFormat(), + CameraParameters::PIXEL_FORMAT_YUV420SP) != 0) { + LOGE("Only yuv420sp preview is supported"); return -1; } - if (strcmp(params.getPictureFormat(), "jpeg") != 0) { + if (strcmp(params.getPictureFormat(), + CameraParameters::PIXEL_FORMAT_JPEG) != 0) { LOGE("Only jpeg still pictures are supported"); return -1; } @@ -379,22 +380,12 @@ void CameraHardwareStub::release() { } -wp<CameraHardwareInterface> CameraHardwareStub::singleton; - sp<CameraHardwareInterface> CameraHardwareStub::createInstance() { - if (singleton != 0) { - sp<CameraHardwareInterface> hardware = singleton.promote(); - if (hardware != 0) { - return hardware; - } - } - sp<CameraHardwareInterface> hardware(new CameraHardwareStub()); - singleton = hardware; - return hardware; + return new CameraHardwareStub(); } -extern "C" sp<CameraHardwareInterface> openCameraHardware() +extern "C" sp<CameraHardwareInterface> openCameraHardwareStub() { return CameraHardwareStub::createInstance(); } diff --git a/camera/libcameraservice/CameraHardwareStub.h b/camera/libcameraservice/CameraHardwareStub.h index 957813a..d194f3c 100644 --- a/camera/libcameraservice/CameraHardwareStub.h +++ b/camera/libcameraservice/CameraHardwareStub.h @@ -67,8 +67,6 @@ private: CameraHardwareStub(); virtual ~CameraHardwareStub(); - static wp<CameraHardwareInterface> singleton; - static const int kBufferCount = 4; class PreviewThread : public Thread { @@ -130,6 +128,8 @@ private: int mCurrentPreviewFrame; }; +extern "C" sp<CameraHardwareInterface> openCameraHardwareStub(); + }; // namespace android #endif diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index 53c77da..e8ab34f 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -16,385 +16,449 @@ ** limitations under the License. */ -//#define LOG_NDEBUG 0 #define LOG_TAG "CameraService" -#include <utils/Log.h> -#include <binder/IServiceManager.h> +#include <stdio.h> +#include <sys/types.h> +#include <pthread.h> + #include <binder/IPCThreadState.h> -#include <utils/String16.h> -#include <utils/Errors.h> +#include <binder/IServiceManager.h> #include <binder/MemoryBase.h> #include <binder/MemoryHeapBase.h> -#include <camera/ICameraService.h> +#include <cutils/atomic.h> +#include <hardware/hardware.h> +#include <media/AudioSystem.h> +#include <media/mediaplayer.h> #include <surfaceflinger/ISurface.h> #include <ui/Overlay.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/String16.h> -#include <hardware/hardware.h> - -#include <media/mediaplayer.h> -#include <media/AudioSystem.h> #include "CameraService.h" - -#include <cutils/atomic.h> +#ifdef USE_CAMERA_STUB +#include "CameraHardwareStub.h" +#endif namespace android { -extern "C" { -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <pthread.h> -#include <signal.h> -} +/* This determines the number of cameras available */ +#if defined(USE_CAMERA_HARDWARE) && defined(USE_CAMERA_STUB) + #define NUM_CAMERAS 2 +#elif defined(USE_CAMERA_HARDWARE) || defined(USE_CAMERA_STUB) + #define NUM_CAMERAS 1 +#else + #error "Should have at least one camera" +#endif -// When you enable this, as well as DEBUG_REFS=1 and -// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp, this will track all -// references to the CameraService::Client in order to catch the case where the -// client is being destroyed while a callback from the CameraHardwareInterface -// is outstanding. This is a serious bug because if we make another call into -// CameraHardwreInterface that itself triggers a callback, we will deadlock. +/* Make sure we have enough array space allocated */ +#if NUM_CAMERAS > MAX_CAMERAS + #error "Need to increase MAX_CAMERAS" +#endif -#define DEBUG_CLIENT_REFERENCES 0 +/* This defines the "open" function for each camera */ +extern "C" typedef sp<CameraHardwareInterface> (*OpenCameraHardwareFunction)(); +static OpenCameraHardwareFunction sOpenCameraTable[] = { +#ifdef USE_CAMERA_HARDWARE + &openCameraHardware, +#endif +#ifdef USE_CAMERA_STUB + &openCameraHardwareStub, +#endif +}; -#define PICTURE_TIMEOUT seconds(5) +// ---------------------------------------------------------------------------- +// Logging support -- this is for debugging only +// Use "adb shell dumpsys media.camera -v 1" to change it. +static volatile int32_t gLogLevel = 0; -#define DEBUG_DUMP_PREVIEW_FRAME_TO_FILE 0 /* n-th frame to write */ -#define DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE 0 -#define DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE 0 -#define DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE 0 +#define LOG1(...) LOGD_IF(gLogLevel >= 1, __VA_ARGS__); +#define LOG2(...) LOGD_IF(gLogLevel >= 2, __VA_ARGS__); -#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE -static int debug_frame_cnt; -#endif +static void setLogLevel(int level) { + android_atomic_write(level, &gLogLevel); +} + +// ---------------------------------------------------------------------------- static int getCallingPid() { return IPCThreadState::self()->getCallingPid(); } -// ---------------------------------------------------------------------------- - -void CameraService::instantiate() { - defaultServiceManager()->addService( - String16("media.camera"), new CameraService()); +static int getCallingUid() { + return IPCThreadState::self()->getCallingUid(); } // ---------------------------------------------------------------------------- -CameraService::CameraService() : - BnCameraService() +// This is ugly and only safe if we never re-create the CameraService, but +// should be ok for now. +static CameraService *gCameraService; + +CameraService::CameraService() +:mSoundRef(0) { - LOGI("CameraService started: pid=%d", getpid()); - mUsers = 0; + LOGI("CameraService started (pid=%d)", getpid()); + + for (int i = 0; i < NUM_CAMERAS; i++) { + setCameraFree(i); + } + + gCameraService = this; } -CameraService::~CameraService() -{ - if (mClient != 0) { - LOGE("mClient was still connected in destructor!"); +CameraService::~CameraService() { + for (int i = 0; i < NUM_CAMERAS; i++) { + if (mBusy[i]) { + LOGE("camera %d is still in use in destructor!", i); + } } + + gCameraService = NULL; } -sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient) -{ +int32_t CameraService::getNumberOfCameras() { + return NUM_CAMERAS; +} + +sp<ICamera> CameraService::connect( + const sp<ICameraClient>& cameraClient, int cameraId) { int callingPid = getCallingPid(); - LOGV("CameraService::connect E (pid %d, client %p)", callingPid, - cameraClient->asBinder().get()); + LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId); - Mutex::Autolock lock(mServiceLock); sp<Client> client; - if (mClient != 0) { - sp<Client> currentClient = mClient.promote(); - if (currentClient != 0) { - sp<ICameraClient> currentCameraClient(currentClient->getCameraClient()); - if (cameraClient->asBinder() == currentCameraClient->asBinder()) { - // This is the same client reconnecting... - LOGV("CameraService::connect X (pid %d, same client %p) is reconnecting...", - callingPid, cameraClient->asBinder().get()); - return currentClient; - } else { - // It's another client... reject it - LOGV("CameraService::connect X (pid %d, new client %p) rejected. " - "(old pid %d, old client %p)", - callingPid, cameraClient->asBinder().get(), - currentClient->mClientPid, currentCameraClient->asBinder().get()); - if (kill(currentClient->mClientPid, 0) == -1 && errno == ESRCH) { - LOGV("The old client is dead!"); - } + if (cameraId < 0 || cameraId >= NUM_CAMERAS) { + LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).", + callingPid, cameraId); + 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 { - // can't promote, the previous client has died... - LOGV("New client (pid %d) connecting, old reference was dangling...", + } else { + LOGW("CameraService::connect X (pid %d) rejected (existing client).", callingPid); - mClient.clear(); + return NULL; + } } + mClient[cameraId].clear(); } - if (mUsers > 0) { - LOGV("Still have client, rejected"); - return client; + if (mBusy[cameraId]) { + LOGW("CameraService::connect X (pid %d) rejected" + " (camera %d is still busy).", callingPid, cameraId); + return NULL; } - // create a new Client object - client = new Client(this, cameraClient, callingPid); - mClient = client; -#if DEBUG_CLIENT_REFERENCES - // Enable tracking for this object, and track increments and decrements of - // the refcount. - client->trackMe(true, true); -#endif - LOGV("CameraService::connect X"); + client = new Client(this, cameraClient, cameraId, callingPid); + mClient[cameraId] = client; + LOG1("CameraService::connect X"); return client; } -void CameraService::removeClient(const sp<ICameraClient>& cameraClient) -{ +void CameraService::removeClient(const sp<ICameraClient>& cameraClient) { int callingPid = getCallingPid(); + LOG1("CameraService::removeClient E (pid %d)", callingPid); - // Declare this outside the lock to make absolutely sure the - // destructor won't be called with the lock held. - sp<Client> client; + for (int i = 0; i < NUM_CAMERAS; i++) { + // Declare this before the lock to make absolutely sure the + // destructor won't be called with the lock held. + sp<Client> client; - Mutex::Autolock lock(mServiceLock); + Mutex::Autolock lock(mServiceLock); - if (mClient == 0) { - // This happens when we have already disconnected. - LOGV("removeClient (pid %d): already disconnected", callingPid); - return; - } + // This happens when we have already disconnected (or this is + // just another unused camera). + if (mClient[i] == 0) continue; - // Promote mClient. It can fail if we are called from this path: - // Client::~Client() -> disconnect() -> removeClient(). - client = mClient.promote(); - if (client == 0) { - LOGV("removeClient (pid %d): no more strong reference", callingPid); - mClient.clear(); - return; + // Promote mClient. It can fail if we are called from this path: + // Client::~Client() -> disconnect() -> removeClient(). + client = mClient[i].promote(); + + if (client == 0) { + mClient[i].clear(); + continue; + } + + if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) { + // Found our camera, clear and leave. + LOG1("removeClient: clear camera %d", i); + mClient[i].clear(); + break; + } } - if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) { - // ugh! that's not our client!! - LOGW("removeClient (pid %d): mClient doesn't match!", callingPid); - } else { - // okay, good, forget about mClient - mClient.clear(); + LOG1("CameraService::removeClient X (pid %d)", callingPid); +} + +sp<CameraService::Client> CameraService::getClientById(int cameraId) { + if (cameraId < 0 || cameraId >= NUM_CAMERAS) return NULL; + return mClient[cameraId].promote(); +} + +void CameraService::instantiate() { + defaultServiceManager()->addService(String16("media.camera"), + new CameraService()); +} + +status_t CameraService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + // Permission checks + switch (code) { + case BnCameraService::CONNECT: + const int pid = getCallingPid(); + const int self_pid = getpid(); + if (pid != self_pid) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.CAMERA"))) { + const int uid = getCallingUid(); + LOGE("Permission Denial: " + "can't use the camera pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + break; } - LOGV("removeClient (pid %d) done", callingPid); + return BnCameraService::onTransact(code, data, reply, flags); } -// The reason we need this count is a new CameraService::connect() request may -// come in while the previous Client's destructor has not been run or is still -// running. If the last strong reference of the previous Client is gone but -// destructor has not been run, we should not allow the new Client to be created -// because we need to wait for the previous Client to tear down the hardware -// first. -void CameraService::incUsers() { - android_atomic_inc(&mUsers); +// The reason we need this busy bit is a new CameraService::connect() request +// may come in while the previous Client's destructor has not been run or is +// still running. If the last strong reference of the previous Client is gone +// but the destructor has not been finished, we should not allow the new Client +// to be created because we need to wait for the previous Client to tear down +// the hardware first. +void CameraService::setCameraBusy(int cameraId) { + android_atomic_write(1, &mBusy[cameraId]); } -void CameraService::decUsers() { - android_atomic_dec(&mUsers); +void CameraService::setCameraFree(int cameraId) { + android_atomic_write(0, &mBusy[cameraId]); } -static sp<MediaPlayer> newMediaPlayer(const char *file) -{ - sp<MediaPlayer> mp = new MediaPlayer(); - if (mp->setDataSource(file, NULL /* headers */) == NO_ERROR) { +// We share the media players for shutter and recording sound for all clients. +// A reference count is kept to determine when we will actually release the +// media players. + +static MediaPlayer* newMediaPlayer(const char *file) { + MediaPlayer* mp = new MediaPlayer(); + if (mp->setDataSource(file, NULL) == NO_ERROR) { mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE); mp->prepare(); } else { - mp.clear(); - LOGE("Failed to load CameraService sounds."); + LOGE("Failed to load CameraService sounds: %s", file); + return NULL; } return mp; } +void CameraService::loadSound() { + Mutex::Autolock lock(mSoundLock); + LOG1("CameraService::loadSound ref=%d", mSoundRef); + if (mSoundRef++) return; + + mSoundPlayer[SOUND_SHUTTER] = newMediaPlayer("/system/media/audio/ui/camera_click.ogg"); + mSoundPlayer[SOUND_RECORDING] = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg"); +} + +void CameraService::releaseSound() { + Mutex::Autolock lock(mSoundLock); + LOG1("CameraService::releaseSound ref=%d", mSoundRef); + if (--mSoundRef) return; + + for (int i = 0; i < NUM_SOUNDS; i++) { + if (mSoundPlayer[i] != 0) { + mSoundPlayer[i]->disconnect(); + mSoundPlayer[i].clear(); + } + } +} + +void CameraService::playSound(sound_kind kind) { + LOG1("playSound(%d)", kind); + Mutex::Autolock lock(mSoundLock); + sp<MediaPlayer> player = mSoundPlayer[kind]; + if (player != 0) { + // do not play the sound if stream volume is 0 + // (typically because ringer mode is silent). + int index; + AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index); + if (index != 0) { + player->seekTo(0); + player->start(); + } + } +} + +// ---------------------------------------------------------------------------- + CameraService::Client::Client(const sp<CameraService>& cameraService, - const sp<ICameraClient>& cameraClient, pid_t clientPid) -{ + const sp<ICameraClient>& cameraClient, int cameraId, int clientPid) { int callingPid = getCallingPid(); - LOGV("Client::Client E (pid %d)", callingPid); + LOG1("Client::Client E (pid %d)", callingPid); + mCameraService = cameraService; mCameraClient = cameraClient; + mCameraId = cameraId; mClientPid = clientPid; - mHardware = openCameraHardware(); + + mHardware = sOpenCameraTable[cameraId](); mUseOverlay = mHardware->useOverlay(); + mMsgEnabled = 0; mHardware->setCallbacks(notifyCallback, dataCallback, dataCallbackTimestamp, - mCameraService.get()); + (void *)cameraId); // Enable zoom, error, and focus messages by default - mHardware->enableMsgType(CAMERA_MSG_ERROR | - CAMERA_MSG_ZOOM | - CAMERA_MSG_FOCUS); - - mMediaPlayerClick = newMediaPlayer("/system/media/audio/ui/camera_click.ogg"); - mMediaPlayerBeep = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg"); + enableMsgType(CAMERA_MSG_ERROR | + CAMERA_MSG_ZOOM | + CAMERA_MSG_FOCUS); mOverlayW = 0; mOverlayH = 0; // Callback is disabled by default mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; mOrientation = 0; - cameraService->incUsers(); - LOGV("Client::Client X (pid %d)", callingPid); + cameraService->setCameraBusy(cameraId); + cameraService->loadSound(); + LOG1("Client::Client X (pid %d)", callingPid); } -status_t CameraService::Client::checkPid() -{ +static void *unregister_surface(void *arg) { + ISurface *surface = (ISurface *)arg; + surface->unregisterBuffers(); + IPCThreadState::self()->flushCommands(); + return NULL; +} + +// tear down the client +CameraService::Client::~Client() { int callingPid = getCallingPid(); - if (mClientPid == callingPid || callingPid == getpid()) return NO_ERROR; - LOGW("Attempt to use locked camera (client %p) from different process " - " (old pid %d, new pid %d)", - getCameraClient()->asBinder().get(), mClientPid, callingPid); - return -EBUSY; + LOG1("Client::~Client E (pid %d, this %p)", callingPid, this); + + if (mSurface != 0 && !mUseOverlay) { + pthread_t thr; + // We unregister the buffers in a different thread because binder does + // not let us make sychronous transactions in a binder destructor (that + // is, upon our reaching a refcount of zero.) + pthread_create(&thr, + NULL, // attr + unregister_surface, + mSurface.get()); + pthread_join(thr, NULL); + } + + // set mClientPid to let disconnet() tear down the hardware + mClientPid = callingPid; + disconnect(); + mCameraService->releaseSound(); + LOG1("Client::~Client X (pid %d, this %p)", callingPid, this); } -status_t CameraService::Client::lock() -{ +// ---------------------------------------------------------------------------- + +status_t CameraService::Client::checkPid() const { + int callingPid = getCallingPid(); + if (callingPid == mClientPid) return NO_ERROR; + if (callingPid == getpid()) { + LOGW("FIXME: use camera from mediaserver without permission."); + return NO_ERROR; + } + LOGW("attempt to use a locked camera from a different process" + " (old pid %d, new pid %d)", mClientPid, callingPid); + return EBUSY; +} + +status_t CameraService::Client::checkPidAndHardware() const { + status_t result = checkPid(); + if (result != NO_ERROR) return result; + if (mHardware == 0) { + LOGE("attempt to use a camera after disconnect() (pid %d)", getCallingPid()); + return INVALID_OPERATION; + } + return NO_ERROR; +} + +status_t CameraService::Client::lock() { int callingPid = getCallingPid(); - LOGV("lock from pid %d (mClientPid %d)", callingPid, mClientPid); - Mutex::Autolock _l(mLock); + LOG1("lock (pid %d)", callingPid); + Mutex::Autolock lock(mLock); + // lock camera to this client if the the camera is unlocked if (mClientPid == 0) { mClientPid = callingPid; return NO_ERROR; } - // returns NO_ERROR if the client already owns the camera, -EBUSY otherwise + + // returns NO_ERROR if the client already owns the camera, EBUSY otherwise return checkPid(); } -status_t CameraService::Client::unlock() -{ +status_t CameraService::Client::unlock() { int callingPid = getCallingPid(); - LOGV("unlock from pid %d (mClientPid %d)", callingPid, mClientPid); - Mutex::Autolock _l(mLock); - // allow anyone to use camera + LOG1("unlock (pid %d)", callingPid); + Mutex::Autolock lock(mLock); + + // allow anyone to use camera (after they lock the camera) status_t result = checkPid(); if (result == NO_ERROR) { mClientPid = 0; - LOGV("clear mCameraClient (pid %d)", callingPid); - // we need to remove the reference so that when app goes - // away, the reference count goes to 0. + LOG1("clear mCameraClient (pid %d)", callingPid); + // we need to remove the reference to ICameraClient so that when the app + // goes away, the reference count goes to 0. mCameraClient.clear(); } return result; } -status_t CameraService::Client::connect(const sp<ICameraClient>& client) -{ +// connect a new client to the camera +status_t CameraService::Client::connect(const sp<ICameraClient>& client) { int callingPid = getCallingPid(); + LOG1("connect E (pid %d)", callingPid); + Mutex::Autolock lock(mLock); - // connect a new process to the camera - LOGV("Client::connect E (pid %d, client %p)", callingPid, client->asBinder().get()); - - // I hate this hack, but things get really ugly when the media recorder - // service is handing back the camera to the app. The ICameraClient - // destructor will be called during the same IPC, making it look like - // the remote client is trying to disconnect. This hack temporarily - // sets the mClientPid to an invalid pid to prevent the hardware from - // being torn down. - { - - // hold a reference to the old client or we will deadlock if the client is - // in the same process and we hold the lock when we remove the reference - sp<ICameraClient> oldClient; - { - Mutex::Autolock _l(mLock); - if (mClientPid != 0 && checkPid() != NO_ERROR) { - LOGW("Tried to connect to locked camera (old pid %d, new pid %d)", - mClientPid, callingPid); - return -EBUSY; - } - oldClient = mCameraClient; - - // did the client actually change? - if ((mCameraClient != NULL) && (client->asBinder() == mCameraClient->asBinder())) { - LOGV("Connect to the same client"); - return NO_ERROR; - } - - mCameraClient = client; - mClientPid = -1; - mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; - LOGV("Connect to the new client (pid %d, client %p)", - callingPid, mCameraClient->asBinder().get()); - } + if (mClientPid != 0 && checkPid() != NO_ERROR) { + LOGW("Tried to connect to a locked camera (old pid %d, new pid %d)", + mClientPid, callingPid); + return EBUSY; + } + if (mCameraClient != 0 && (client->asBinder() == mCameraClient->asBinder())) { + LOG1("Connect to the same client"); + return NO_ERROR; } - // the old client destructor is called when oldClient goes out of scope - // now we set the new PID to lock the interface again + + mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; mClientPid = callingPid; + mCameraClient = client; + LOG1("connect X (pid %d)", callingPid); return NO_ERROR; } -#if HAVE_ANDROID_OS -static void *unregister_surface(void *arg) -{ - ISurface *surface = (ISurface *)arg; - surface->unregisterBuffers(); - IPCThreadState::self()->flushCommands(); - return NULL; -} -#endif - -CameraService::Client::~Client() -{ +void CameraService::Client::disconnect() { int callingPid = getCallingPid(); + LOG1("disconnect E (pid %d)", callingPid); + Mutex::Autolock lock(mLock); - // tear down client - LOGV("Client::~Client E (pid %d, client %p)", - callingPid, getCameraClient()->asBinder().get()); - if (mSurface != 0 && !mUseOverlay) { -#if HAVE_ANDROID_OS - pthread_t thr; - // We unregister the buffers in a different thread because binder does - // not let us make sychronous transactions in a binder destructor (that - // is, upon our reaching a refcount of zero.) - pthread_create(&thr, NULL, - unregister_surface, - mSurface.get()); - pthread_join(thr, NULL); -#else - mSurface->unregisterBuffers(); -#endif - } - - if (mMediaPlayerBeep.get() != NULL) { - mMediaPlayerBeep->disconnect(); - mMediaPlayerBeep.clear(); - } - if (mMediaPlayerClick.get() != NULL) { - mMediaPlayerClick->disconnect(); - mMediaPlayerClick.clear(); + if (checkPid() != NO_ERROR) { + LOGW("different client - don't disconnect"); + return; } - // make sure we tear down the hardware - mClientPid = callingPid; - disconnect(); - LOGV("Client::~Client X (pid %d)", mClientPid); -} - -void CameraService::Client::disconnect() -{ - int callingPid = getCallingPid(); - - LOGV("Client::disconnect() E (pid %d client %p)", - callingPid, getCameraClient()->asBinder().get()); - - Mutex::Autolock lock(mLock); if (mClientPid <= 0) { - LOGV("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid); - return; - } - if (checkPid() != NO_ERROR) { - LOGV("Different client - don't disconnect"); + LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid); return; } @@ -402,519 +466,564 @@ void CameraService::Client::disconnect() // from the user directly, or called by the destructor. if (mHardware == 0) return; - LOGV("hardware teardown"); + LOG1("hardware teardown"); // Before destroying mHardware, we must make sure it's in the // idle state. + // Turn off all messages. + disableMsgType(CAMERA_MSG_ALL_MSGS); mHardware->stopPreview(); - // Cancel all picture callbacks. - mHardware->disableMsgType(CAMERA_MSG_SHUTTER | - CAMERA_MSG_POSTVIEW_FRAME | - CAMERA_MSG_RAW_IMAGE | - CAMERA_MSG_COMPRESSED_IMAGE); mHardware->cancelPicture(); - // Turn off remaining messages. - mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS); // Release the hardware resources. mHardware->release(); // Release the held overlay resources. - if (mUseOverlay) - { + if (mUseOverlay) { mOverlayRef = 0; } mHardware.clear(); mCameraService->removeClient(mCameraClient); - mCameraService->decUsers(); + mCameraService->setCameraFree(mCameraId); - LOGV("Client::disconnect() X (pid %d)", callingPid); + LOG1("disconnect X (pid %d)", callingPid); } -// pass the buffered ISurface to the camera service -status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) -{ - LOGV("setPreviewDisplay(%p) (pid %d)", - ((surface == NULL) ? NULL : surface.get()), getCallingPid()); +// ---------------------------------------------------------------------------- + +// set the ISurface that the preview will use +status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) { + LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid()); Mutex::Autolock lock(mLock); - status_t result = checkPid(); + status_t result = checkPidAndHardware(); if (result != NO_ERROR) return result; - Mutex::Autolock surfaceLock(mSurfaceLock); result = NO_ERROR; - // asBinder() is safe on NULL (returns NULL) - if (surface->asBinder() != mSurface->asBinder()) { - if (mSurface != 0) { - LOGV("clearing old preview surface %p", mSurface.get()); - if ( !mUseOverlay) - { - mSurface->unregisterBuffers(); - } - else - { - // Force the destruction of any previous overlay - sp<Overlay> dummy; - mHardware->setOverlay( dummy ); - } - } - mSurface = surface; - mOverlayRef = 0; - // If preview has been already started, set overlay or register preview - // buffers now. - if (mHardware->previewEnabled()) { - if (mUseOverlay) { - result = setOverlay(); - } else if (mSurface != 0) { - result = registerPreviewBuffers(); - } - } - } - return result; -} - -// set the preview callback flag to affect how the received frames from -// preview are handled. -void CameraService::Client::setPreviewCallbackFlag(int callback_flag) -{ - LOGV("setPreviewCallbackFlag (pid %d)", getCallingPid()); - Mutex::Autolock lock(mLock); - if (checkPid() != NO_ERROR) return; - mPreviewCallbackFlag = callback_flag; - - if(mUseOverlay) { - if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK) - mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME); - else - mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME); - } -} -// start preview mode -status_t CameraService::Client::startCameraMode(camera_mode mode) -{ - int callingPid = getCallingPid(); - - LOGV("startCameraMode(%d) (pid %d)", mode, callingPid); - - /* we cannot call into mHardware with mLock held because - * mHardware has callbacks onto us which acquire this lock - */ - - Mutex::Autolock lock(mLock); - status_t result = checkPid(); - if (result != NO_ERROR) return result; - - if (mHardware == 0) { - LOGE("mHardware is NULL, returning."); - return INVALID_OPERATION; + // return if no change in surface. + // asBinder() is safe on NULL (returns NULL) + if (surface->asBinder() == mSurface->asBinder()) { + return result; } - switch(mode) { - case CAMERA_RECORDING_MODE: - if (mSurface == 0) { - LOGE("setPreviewDisplay must be called before startRecordingMode."); - return INVALID_OPERATION; + if (mSurface != 0) { + LOG1("clearing old preview surface %p", mSurface.get()); + if (mUseOverlay) { + // Force the destruction of any previous overlay + sp<Overlay> dummy; + mHardware->setOverlay(dummy); + } else { + mSurface->unregisterBuffers(); } - return startRecordingMode(); - - default: // CAMERA_PREVIEW_MODE - if (mSurface == 0) { - LOGV("mSurface is not set yet."); + } + mSurface = surface; + mOverlayRef = 0; + // If preview has been already started, set overlay or register preview + // buffers now. + if (mHardware->previewEnabled()) { + if (mUseOverlay) { + result = setOverlay(); + } else if (mSurface != 0) { + result = registerPreviewBuffers(); } - return startPreviewMode(); } -} -status_t CameraService::Client::startRecordingMode() -{ - LOGV("startRecordingMode (pid %d)", getCallingPid()); - - status_t ret = UNKNOWN_ERROR; + return result; +} - // if preview has not been started, start preview first - if (!mHardware->previewEnabled()) { - ret = startPreviewMode(); - if (ret != NO_ERROR) { - return ret; - } - } +status_t CameraService::Client::registerPreviewBuffers() { + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); - // if recording has been enabled, nothing needs to be done - if (mHardware->recordingEnabled()) { - return NO_ERROR; - } + // FIXME: don't use a hardcoded format here. + ISurface::BufferHeap buffers(w, h, w, h, + HAL_PIXEL_FORMAT_YCrCb_420_SP, + mOrientation, + 0, + mHardware->getPreviewHeap()); - // start recording mode - ret = mHardware->startRecording(); - if (ret != NO_ERROR) { - LOGE("mHardware->startRecording() failed with status %d", ret); + status_t result = mSurface->registerBuffers(buffers); + if (result != NO_ERROR) { + LOGE("registerBuffers failed with status %d", result); } - return ret; + return result; } -status_t CameraService::Client::setOverlay() -{ - LOGV("setOverlay"); +status_t CameraService::Client::setOverlay() { int w, h; CameraParameters params(mHardware->getParameters()); params.getPreviewSize(&w, &h); - if ( w != mOverlayW || h != mOverlayH ) - { + if (w != mOverlayW || h != mOverlayH) { // Force the destruction of any previous overlay sp<Overlay> dummy; - mHardware->setOverlay( dummy ); + mHardware->setOverlay(dummy); mOverlayRef = 0; } - status_t ret = NO_ERROR; - if (mSurface != 0) { - if (mOverlayRef.get() == NULL) { - + status_t result = NO_ERROR; + if (mSurface == 0) { + result = mHardware->setOverlay(NULL); + } else { + if (mOverlayRef == 0) { // FIXME: // Surfaceflinger may hold onto the previous overlay reference for some // time after we try to destroy it. retry a few times. In the future, we // should make the destroy call block, or possibly specify that we can - // wait in the createOverlay call if the previous overlay is in the + // wait in the createOverlay call if the previous overlay is in the // process of being destroyed. for (int retry = 0; retry < 50; ++retry) { mOverlayRef = mSurface->createOverlay(w, h, OVERLAY_FORMAT_DEFAULT, mOrientation); - if (mOverlayRef != NULL) break; + if (mOverlayRef != 0) break; LOGW("Overlay create failed - retrying"); usleep(20000); } - if ( mOverlayRef.get() == NULL ) - { + if (mOverlayRef == 0) { LOGE("Overlay Creation Failed!"); return -EINVAL; } - ret = mHardware->setOverlay(new Overlay(mOverlayRef)); + result = mHardware->setOverlay(new Overlay(mOverlayRef)); } - } else { - ret = mHardware->setOverlay(NULL); } - if (ret != NO_ERROR) { - LOGE("mHardware->setOverlay() failed with status %d\n", ret); + if (result != NO_ERROR) { + LOGE("mHardware->setOverlay() failed with status %d\n", result); + return result; } mOverlayW = w; mOverlayH = h; - return ret; + return result; } -status_t CameraService::Client::registerPreviewBuffers() -{ - int w, h; - CameraParameters params(mHardware->getParameters()); - params.getPreviewSize(&w, &h); +// set the preview callback flag to affect how the received frames from +// preview are handled. +void CameraService::Client::setPreviewCallbackFlag(int callback_flag) { + LOG1("setPreviewCallbackFlag(%d) (pid %d)", callback_flag, getCallingPid()); + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return; - // don't use a hardcoded format here - ISurface::BufferHeap buffers(w, h, w, h, - HAL_PIXEL_FORMAT_YCrCb_420_SP, - mOrientation, - 0, - mHardware->getPreviewHeap()); + mPreviewCallbackFlag = callback_flag; - status_t ret = mSurface->registerBuffers(buffers); - if (ret != NO_ERROR) { - LOGE("registerBuffers failed with status %d", ret); + // If we don't use overlay, we always need the preview frame for display. + // If we do use overlay, we only need the preview frame if the user + // wants the data. + if (mUseOverlay) { + if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK) { + enableMsgType(CAMERA_MSG_PREVIEW_FRAME); + } else { + disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + } } - return ret; } -status_t CameraService::Client::startPreviewMode() -{ - LOGV("startPreviewMode (pid %d)", getCallingPid()); +// start preview mode +status_t CameraService::Client::startPreview() { + LOG1("startPreview (pid %d)", getCallingPid()); + return startCameraMode(CAMERA_PREVIEW_MODE); +} + +// start recording mode +status_t CameraService::Client::startRecording() { + LOG1("startRecording (pid %d)", getCallingPid()); + return startCameraMode(CAMERA_RECORDING_MODE); +} + +// start preview or recording +status_t CameraService::Client::startCameraMode(camera_mode mode) { + LOG1("startCameraMode(%d)", mode); + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + switch(mode) { + case CAMERA_PREVIEW_MODE: + if (mSurface == 0) { + LOG1("mSurface is not set yet."); + // still able to start preview in this case. + } + return startPreviewMode(); + case CAMERA_RECORDING_MODE: + if (mSurface == 0) { + LOGE("mSurface must be set before startRecordingMode."); + return INVALID_OPERATION; + } + return startRecordingMode(); + default: + return UNKNOWN_ERROR; + } +} + +status_t CameraService::Client::startPreviewMode() { + LOG1("startPreviewMode"); + status_t result = NO_ERROR; // if preview has been enabled, nothing needs to be done if (mHardware->previewEnabled()) { return NO_ERROR; } - // start preview mode -#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE - debug_frame_cnt = 0; -#endif - status_t ret = NO_ERROR; - if (mUseOverlay) { // If preview display has been set, set overlay now. if (mSurface != 0) { - ret = setOverlay(); + result = setOverlay(); } - if (ret != NO_ERROR) return ret; - ret = mHardware->startPreview(); + if (result != NO_ERROR) return result; + result = mHardware->startPreview(); } else { - mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME); - ret = mHardware->startPreview(); - if (ret != NO_ERROR) return ret; + enableMsgType(CAMERA_MSG_PREVIEW_FRAME); + result = mHardware->startPreview(); + if (result != NO_ERROR) return result; // If preview display has been set, register preview buffers now. if (mSurface != 0) { - // Unregister here because the surface registered with raw heap. + // Unregister here because the surface may be previously registered + // with the raw (snapshot) heap. mSurface->unregisterBuffers(); - ret = registerPreviewBuffers(); + result = registerPreviewBuffers(); } } - return ret; + return result; } -status_t CameraService::Client::startPreview() -{ - LOGV("startPreview (pid %d)", getCallingPid()); - - return startCameraMode(CAMERA_PREVIEW_MODE); -} +status_t CameraService::Client::startRecordingMode() { + LOG1("startRecordingMode"); + status_t result = NO_ERROR; -status_t CameraService::Client::startRecording() -{ - LOGV("startRecording (pid %d)", getCallingPid()); + // if recording has been enabled, nothing needs to be done + if (mHardware->recordingEnabled()) { + return NO_ERROR; + } - if (mMediaPlayerBeep.get() != NULL) { - // do not play record jingle if stream volume is 0 - // (typically because ringer mode is silent). - int index; - AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index); - if (index != 0) { - mMediaPlayerBeep->seekTo(0); - mMediaPlayerBeep->start(); + // if preview has not been started, start preview first + if (!mHardware->previewEnabled()) { + result = startPreviewMode(); + if (result != NO_ERROR) { + return result; } } - mHardware->enableMsgType(CAMERA_MSG_VIDEO_FRAME); - - return startCameraMode(CAMERA_RECORDING_MODE); + // start recording mode + enableMsgType(CAMERA_MSG_VIDEO_FRAME); + mCameraService->playSound(SOUND_RECORDING); + result = mHardware->startRecording(); + if (result != NO_ERROR) { + LOGE("mHardware->startRecording() failed with status %d", result); + } + return result; } // stop preview mode -void CameraService::Client::stopPreview() -{ - LOGV("stopPreview (pid %d)", getCallingPid()); - - // hold main lock during state transition - { - Mutex::Autolock lock(mLock); - if (checkPid() != NO_ERROR) return; - - if (mHardware == 0) { - LOGE("mHardware is NULL, returning."); - return; - } +void CameraService::Client::stopPreview() { + LOG1("stopPreview (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return; - mHardware->stopPreview(); - mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME); - LOGV("stopPreview(), hardware stopped OK"); + disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + mHardware->stopPreview(); - if (mSurface != 0 && !mUseOverlay) { - mSurface->unregisterBuffers(); - } + if (mSurface != 0 && !mUseOverlay) { + mSurface->unregisterBuffers(); } - // hold preview buffer lock - { - Mutex::Autolock lock(mPreviewLock); - mPreviewBuffer.clear(); - } + mPreviewBuffer.clear(); } // stop recording mode -void CameraService::Client::stopRecording() -{ - LOGV("stopRecording (pid %d)", getCallingPid()); - - // hold main lock during state transition - { - Mutex::Autolock lock(mLock); - if (checkPid() != NO_ERROR) return; - - if (mHardware == 0) { - LOGE("mHardware is NULL, returning."); - return; - } - - if (mMediaPlayerBeep.get() != NULL) { - mMediaPlayerBeep->seekTo(0); - mMediaPlayerBeep->start(); - } +void CameraService::Client::stopRecording() { + LOG1("stopRecording (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return; - mHardware->stopRecording(); - mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME); - LOGV("stopRecording(), hardware stopped OK"); - } + mCameraService->playSound(SOUND_RECORDING); + disableMsgType(CAMERA_MSG_VIDEO_FRAME); + mHardware->stopRecording(); - // hold preview buffer lock - { - Mutex::Autolock lock(mPreviewLock); - mPreviewBuffer.clear(); - } + mPreviewBuffer.clear(); } // release a recording frame -void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem) -{ +void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem) { Mutex::Autolock lock(mLock); - if (checkPid() != NO_ERROR) return; - - if (mHardware == 0) { - LOGE("mHardware is NULL, returning."); - return; - } - + if (checkPidAndHardware() != NO_ERROR) return; mHardware->releaseRecordingFrame(mem); } -bool CameraService::Client::previewEnabled() -{ +bool CameraService::Client::previewEnabled() { + LOG1("previewEnabled (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); - if (mHardware == 0) return false; + if (checkPidAndHardware() != NO_ERROR) return false; return mHardware->previewEnabled(); } -bool CameraService::Client::recordingEnabled() -{ +bool CameraService::Client::recordingEnabled() { + LOG1("recordingEnabled (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); - if (mHardware == 0) return false; + if (checkPidAndHardware() != NO_ERROR) return false; return mHardware->recordingEnabled(); } -// Safely retrieves a strong pointer to the client during a hardware callback. -sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) -{ - sp<Client> client = 0; - CameraService *service = static_cast<CameraService*>(user); - if (service != NULL) { - Mutex::Autolock ourLock(service->mServiceLock); - if (service->mClient != 0) { - client = service->mClient.promote(); - if (client == 0) { - LOGE("getClientFromCookie: client appears to have died"); - service->mClient.clear(); - } - } else { - LOGE("getClientFromCookie: got callback but client was NULL"); - } - } - return client; -} +status_t CameraService::Client::autoFocus() { + LOG1("autoFocus (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; -#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE || \ - DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE || \ - DEBUG_DUMP_PREVIEW_FRAME_TO_FILE -static void dump_to_file(const char *fname, - uint8_t *buf, uint32_t size) -{ - int nw, cnt = 0; - uint32_t written = 0; + return mHardware->autoFocus(); +} - LOGV("opening file [%s]\n", fname); - int fd = open(fname, O_RDWR | O_CREAT); - if (fd < 0) { - LOGE("failed to create file [%s]: %s", fname, strerror(errno)); - return; - } +status_t CameraService::Client::cancelAutoFocus() { + LOG1("cancelAutoFocus (pid %d)", getCallingPid()); - LOGV("writing %d bytes to file [%s]\n", size, fname); - while (written < size) { - nw = ::write(fd, - buf + written, - size - written); - if (nw < 0) { - LOGE("failed to write to file [%s]: %s", - fname, strerror(errno)); - break; - } - written += nw; - cnt++; - } - LOGV("done writing %d bytes to file [%s] in %d passes\n", - size, fname, cnt); - ::close(fd); + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + return mHardware->cancelAutoFocus(); } -#endif -status_t CameraService::Client::autoFocus() -{ - LOGV("autoFocus (pid %d)", getCallingPid()); +// take a picture - image is returned in callback +status_t CameraService::Client::takePicture() { + LOG1("takePicture (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); - status_t result = checkPid(); + status_t result = checkPidAndHardware(); if (result != NO_ERROR) return result; - if (mHardware == 0) { - LOGE("mHardware is NULL, returning."); - return INVALID_OPERATION; - } + enableMsgType(CAMERA_MSG_SHUTTER | + CAMERA_MSG_POSTVIEW_FRAME | + CAMERA_MSG_RAW_IMAGE | + CAMERA_MSG_COMPRESSED_IMAGE); - return mHardware->autoFocus(); + return mHardware->takePicture(); } -status_t CameraService::Client::cancelAutoFocus() -{ - LOGV("cancelAutoFocus (pid %d)", getCallingPid()); +// set preview/capture parameters - key/value pairs +status_t CameraService::Client::setParameters(const String8& params) { + LOG1("setParameters (pid %d) (%s)", getCallingPid(), params.string()); Mutex::Autolock lock(mLock); - status_t result = checkPid(); + status_t result = checkPidAndHardware(); if (result != NO_ERROR) return result; - if (mHardware == 0) { - LOGE("mHardware is NULL, returning."); - return INVALID_OPERATION; - } - - return mHardware->cancelAutoFocus(); + CameraParameters p(params); + return mHardware->setParameters(p); } -// take a picture - image is returned in callback -status_t CameraService::Client::takePicture() -{ - LOGV("takePicture (pid %d)", getCallingPid()); +// get preview/capture parameters - key/value pairs +String8 CameraService::Client::getParameters() const { + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return String8(); + String8 params(mHardware->getParameters().flatten()); + LOG1("getParameters (pid %d) (%s)", getCallingPid(), params.string()); + return params; +} + +status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) { + LOG1("sendCommand (pid %d)", getCallingPid()); Mutex::Autolock lock(mLock); - status_t result = checkPid(); + status_t result = checkPidAndHardware(); if (result != NO_ERROR) return result; - if (mHardware == 0) { - LOGE("mHardware is NULL, returning."); - return INVALID_OPERATION; + if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) { + // The orientation cannot be set during preview. + if (mHardware->previewEnabled()) { + return INVALID_OPERATION; + } + switch (arg1) { + case 0: + mOrientation = ISurface::BufferHeap::ROT_0; + break; + case 90: + mOrientation = ISurface::BufferHeap::ROT_90; + break; + case 180: + mOrientation = ISurface::BufferHeap::ROT_180; + break; + case 270: + mOrientation = ISurface::BufferHeap::ROT_270; + break; + default: + return BAD_VALUE; + } + return OK; } - mHardware->enableMsgType(CAMERA_MSG_SHUTTER | - CAMERA_MSG_POSTVIEW_FRAME | - CAMERA_MSG_RAW_IMAGE | - CAMERA_MSG_COMPRESSED_IMAGE); + return mHardware->sendCommand(cmd, arg1, arg2); +} - return mHardware->takePicture(); +// ---------------------------------------------------------------------------- + +void CameraService::Client::enableMsgType(int32_t msgType) { + android_atomic_or(msgType, &mMsgEnabled); + mHardware->enableMsgType(msgType); } -// snapshot taken -void CameraService::Client::handleShutter( - image_rect_type *size // The width and height of yuv picture for - // registerBuffer. If this is NULL, use the picture - // size from parameters. -) -{ - // Play shutter sound. - if (mMediaPlayerClick.get() != NULL) { - // do not play shutter sound if stream volume is 0 - // (typically because ringer mode is silent). - int index; - AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index); - if (index != 0) { - mMediaPlayerClick->seekTo(0); - mMediaPlayerClick->start(); +void CameraService::Client::disableMsgType(int32_t msgType) { + android_atomic_and(~msgType, &mMsgEnabled); + mHardware->disableMsgType(msgType); +} + +#define CHECK_MESSAGE_INTERVAL 10 // 10ms +bool CameraService::Client::lockIfMessageWanted(int32_t msgType) { + int sleepCount = 0; + while (mMsgEnabled & msgType) { + if (mLock.tryLock() == NO_ERROR) { + if (sleepCount > 0) { + LOG1("lockIfMessageWanted(%d): waited for %d ms", + msgType, sleepCount * CHECK_MESSAGE_INTERVAL); + } + return true; } + if (sleepCount++ == 0) { + LOG1("lockIfMessageWanted(%d): enter sleep", msgType); + } + usleep(CHECK_MESSAGE_INTERVAL * 1000); + } + LOGW("lockIfMessageWanted(%d): dropped unwanted message", msgType); + return false; +} + +// ---------------------------------------------------------------------------- + +// Converts from a raw pointer to the client to a strong pointer during a +// hardware callback. This requires the callbacks only happen when the client +// is still alive. +sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) { + sp<Client> client = gCameraService->getClientById((int) user); + + // This could happen if the Client is in the process of shutting down (the + // last strong reference is gone, but the destructor hasn't finished + // stopping the hardware). + if (client == 0) return NULL; + + // The checks below are not necessary and are for debugging only. + if (client->mCameraService.get() != gCameraService) { + LOGE("mismatch service!"); + return NULL; } + if (client->mHardware == 0) { + LOGE("mHardware == 0: callback after disconnect()?"); + return NULL; + } + + return client; +} + +// Callback messages can be dispatched to internal handlers or pass to our +// client's callback functions, depending on the message type. +// +// notifyCallback: +// CAMERA_MSG_SHUTTER handleShutter +// (others) c->notifyCallback +// dataCallback: +// CAMERA_MSG_PREVIEW_FRAME handlePreviewData +// CAMERA_MSG_POSTVIEW_FRAME handlePostview +// CAMERA_MSG_RAW_IMAGE handleRawPicture +// CAMERA_MSG_COMPRESSED_IMAGE handleCompressedPicture +// (others) c->dataCallback +// dataCallbackTimestamp +// (others) c->dataCallbackTimestamp +// +// NOTE: the *Callback functions grab mLock of the client before passing +// control to handle* functions. So the handle* functions must release the +// lock before calling the ICameraClient's callbacks, so those callbacks can +// invoke methods in the Client class again (For example, the preview frame +// callback may want to releaseRecordingFrame). The handle* functions must +// release the lock after all accesses to member variables, so it must be +// handled very carefully. + +void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, + int32_t ext2, void* user) { + LOG2("notifyCallback(%d)", msgType); + + sp<Client> client = getClientFromCookie(user); + if (client == 0) return; + if (!client->lockIfMessageWanted(msgType)) return; + + switch (msgType) { + case CAMERA_MSG_SHUTTER: + // ext1 is the dimension of the yuv picture. + client->handleShutter((image_rect_type *)ext1); + break; + default: + client->handleGenericNotify(msgType, ext1, ext2); + break; + } +} + +void CameraService::Client::dataCallback(int32_t msgType, + const sp<IMemory>& dataPtr, void* user) { + LOG2("dataCallback(%d)", msgType); + + sp<Client> client = getClientFromCookie(user); + if (client == 0) return; + if (!client->lockIfMessageWanted(msgType)) return; + + if (dataPtr == 0) { + LOGE("Null data returned in data callback"); + client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); + return; + } + + switch (msgType) { + case CAMERA_MSG_PREVIEW_FRAME: + client->handlePreviewData(dataPtr); + break; + case CAMERA_MSG_POSTVIEW_FRAME: + client->handlePostview(dataPtr); + break; + case CAMERA_MSG_RAW_IMAGE: + client->handleRawPicture(dataPtr); + break; + case CAMERA_MSG_COMPRESSED_IMAGE: + client->handleCompressedPicture(dataPtr); + break; + default: + client->handleGenericData(msgType, dataPtr); + break; + } +} + +void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, + int32_t msgType, const sp<IMemory>& dataPtr, void* user) { + LOG2("dataCallbackTimestamp(%d)", msgType); + + sp<Client> client = getClientFromCookie(user); + if (client == 0) return; + if (!client->lockIfMessageWanted(msgType)) return; + + if (dataPtr == 0) { + LOGE("Null data returned in data with timestamp callback"); + client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); + return; + } + + client->handleGenericDataTimestamp(timestamp, msgType, dataPtr); +} + +// snapshot taken callback +// "size" is the width and height of yuv picture for registerBuffer. +// If it is NULL, use the picture size from parameters. +void CameraService::Client::handleShutter(image_rect_type *size) { + mCameraService->playSound(SOUND_SHUTTER); + // Screen goes black after the buffer is unregistered. if (mSurface != 0 && !mUseOverlay) { mSurface->unregisterBuffers(); } sp<ICameraClient> c = mCameraClient; - if (c != NULL) { + if (c != 0) { + mLock.unlock(); c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0); + if (!lockIfMessageWanted(CAMERA_MSG_SHUTTER)) return; } - mHardware->disableMsgType(CAMERA_MSG_SHUTTER); + disableMsgType(CAMERA_MSG_SHUTTER); // It takes some time before yuvPicture callback to be called. // Register the buffer for raw image here to reduce latency. @@ -928,7 +1037,7 @@ void CameraService::Client::handleShutter( h = size->height; w &= ~1; h &= ~1; - LOGV("Snapshot image width=%d, height=%d", w, h); + LOG1("Snapshot image width=%d, height=%d", w, h); } // FIXME: don't use hardcoded format constants here ISurface::BufferHeap buffers(w, h, w, h, @@ -937,37 +1046,18 @@ void CameraService::Client::handleShutter( mSurface->registerBuffers(buffers); } + + mLock.unlock(); } // preview callback - frame buffer update -void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) -{ +void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) { ssize_t offset; size_t size; sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); -#if DEBUG_HEAP_LEAKS && 0 // debugging - if (gWeakHeap == NULL) { - if (gWeakHeap != heap) { - LOGV("SETTING PREVIEW HEAP"); - heap->trackMe(true, true); - gWeakHeap = heap; - } - } -#endif -#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE - { - if (debug_frame_cnt++ == DEBUG_DUMP_PREVIEW_FRAME_TO_FILE) { - dump_to_file("/data/preview.yuv", - (uint8_t *)heap->base() + offset, size); - } - } -#endif - - if (!mUseOverlay) - { - Mutex::Autolock surfaceLock(mSurfaceLock); - if (mSurface != NULL) { + if (!mUseOverlay) { + if (mSurface != 0) { mSurface->postBuffer(offset); } } @@ -978,7 +1068,8 @@ void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) // is callback enabled? if (!(flags & FRAME_CALLBACK_FLAG_ENABLE_MASK)) { // If the enable bit is off, the copy-out and one-shot bits are ignored - LOGV("frame callback is diabled"); + LOG2("frame callback is disabled"); + mLock.unlock(); return; } @@ -986,61 +1077,49 @@ void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) sp<ICameraClient> c = mCameraClient; // clear callback flags if no client or one-shot mode - if ((c == NULL) || (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) { - LOGV("Disable preview callback"); + if (c == 0 || (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) { + LOG2("Disable preview callback"); mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK | - FRAME_CALLBACK_FLAG_COPY_OUT_MASK | - FRAME_CALLBACK_FLAG_ENABLE_MASK); - // TODO: Shouldn't we use this API for non-overlay hardware as well? - if (mUseOverlay) - mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + FRAME_CALLBACK_FLAG_COPY_OUT_MASK | + FRAME_CALLBACK_FLAG_ENABLE_MASK); + if (mUseOverlay) { + disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + } } - // Is the received frame copied out or not? - if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) { - LOGV("frame is copied"); - copyFrameAndPostCopiedFrame(c, heap, offset, size); + if (c != 0) { + // Is the received frame copied out or not? + if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) { + LOG2("frame is copied"); + copyFrameAndPostCopiedFrame(c, heap, offset, size); + } else { + LOG2("frame is forwarded"); + mLock.unlock(); + c->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem); + } } else { - LOGV("frame is forwarded"); - c->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem); + mLock.unlock(); } } // picture callback - postview image ready -void CameraService::Client::handlePostview(const sp<IMemory>& mem) -{ -#if DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE // for testing pursposes only - { - ssize_t offset; - size_t size; - sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); - dump_to_file("/data/postview.yuv", - (uint8_t *)heap->base() + offset, size); - } -#endif +void CameraService::Client::handlePostview(const sp<IMemory>& mem) { + disableMsgType(CAMERA_MSG_POSTVIEW_FRAME); sp<ICameraClient> c = mCameraClient; - if (c != NULL) { + mLock.unlock(); + if (c != 0) { c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem); } - mHardware->disableMsgType(CAMERA_MSG_POSTVIEW_FRAME); } // picture callback - raw image ready -void CameraService::Client::handleRawPicture(const sp<IMemory>& mem) -{ +void CameraService::Client::handleRawPicture(const sp<IMemory>& mem) { + disableMsgType(CAMERA_MSG_RAW_IMAGE); + ssize_t offset; size_t size; sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); -#if DEBUG_HEAP_LEAKS && 0 // debugging - gWeakHeap = heap; // debugging -#endif - - //LOGV("handleRawPicture(%d, %d)", offset, size); -#if DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE // for testing pursposes only - dump_to_file("/data/photo.yuv", - (uint8_t *)heap->base() + offset, size); -#endif // Put the YUV version of the snapshot in the preview display. if (mSurface != 0 && !mUseOverlay) { @@ -1048,250 +1127,90 @@ void CameraService::Client::handleRawPicture(const sp<IMemory>& mem) } sp<ICameraClient> c = mCameraClient; - if (c != NULL) { + mLock.unlock(); + if (c != 0) { c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem); } - mHardware->disableMsgType(CAMERA_MSG_RAW_IMAGE); } // picture callback - compressed picture ready -void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem) -{ -#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE // for testing pursposes only - { - ssize_t offset; - size_t size; - sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); - dump_to_file("/data/photo.jpg", - (uint8_t *)heap->base() + offset, size); - } -#endif +void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem) { + disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE); sp<ICameraClient> c = mCameraClient; - if (c != NULL) { + mLock.unlock(); + if (c != 0) { c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem); } - mHardware->disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE); } -void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user) -{ - LOGV("notifyCallback(%d)", msgType); - sp<Client> client = getClientFromCookie(user); - if (client == 0) { - return; - } - - switch (msgType) { - case CAMERA_MSG_SHUTTER: - // ext1 is the dimension of the yuv picture. - client->handleShutter((image_rect_type *)ext1); - break; - default: - sp<ICameraClient> c = client->mCameraClient; - if (c != NULL) { - c->notifyCallback(msgType, ext1, ext2); - } - break; - } - -#if DEBUG_CLIENT_REFERENCES - if (client->getStrongCount() == 1) { - LOGE("++++++++++++++++ (NOTIFY CALLBACK) THIS WILL CAUSE A LOCKUP!"); - client->printRefs(); +void CameraService::Client::handleGenericNotify(int32_t msgType, + int32_t ext1, int32_t ext2) { + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { + c->notifyCallback(msgType, ext1, ext2); } -#endif } -void CameraService::Client::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user) -{ - LOGV("dataCallback(%d)", msgType); - - sp<Client> client = getClientFromCookie(user); - if (client == 0) { - return; - } - - sp<ICameraClient> c = client->mCameraClient; - if (dataPtr == NULL) { - LOGE("Null data returned in data callback"); - if (c != NULL) { - c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); - c->dataCallback(msgType, NULL); - } - return; - } - - switch (msgType) { - case CAMERA_MSG_PREVIEW_FRAME: - client->handlePreviewData(dataPtr); - break; - case CAMERA_MSG_POSTVIEW_FRAME: - client->handlePostview(dataPtr); - break; - case CAMERA_MSG_RAW_IMAGE: - client->handleRawPicture(dataPtr); - break; - case CAMERA_MSG_COMPRESSED_IMAGE: - client->handleCompressedPicture(dataPtr); - break; - default: - if (c != NULL) { - c->dataCallback(msgType, dataPtr); - } - break; - } - -#if DEBUG_CLIENT_REFERENCES - if (client->getStrongCount() == 1) { - LOGE("++++++++++++++++ (DATA CALLBACK) THIS WILL CAUSE A LOCKUP!"); - client->printRefs(); +void CameraService::Client::handleGenericData(int32_t msgType, + const sp<IMemory>& dataPtr) { + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { + c->dataCallback(msgType, dataPtr); } -#endif } -void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, - const sp<IMemory>& dataPtr, void* user) -{ - LOGV("dataCallbackTimestamp(%d)", msgType); - - sp<Client> client = getClientFromCookie(user); - if (client == 0) { - return; - } - sp<ICameraClient> c = client->mCameraClient; - - if (dataPtr == NULL) { - LOGE("Null data returned in data with timestamp callback"); - if (c != NULL) { - c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); - c->dataCallbackTimestamp(0, msgType, NULL); - } - return; - } - - if (c != NULL) { +void CameraService::Client::handleGenericDataTimestamp(nsecs_t timestamp, + int32_t msgType, const sp<IMemory>& dataPtr) { + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { c->dataCallbackTimestamp(timestamp, msgType, dataPtr); } - -#if DEBUG_CLIENT_REFERENCES - if (client->getStrongCount() == 1) { - LOGE("++++++++++++++++ (DATA CALLBACK TIMESTAMP) THIS WILL CAUSE A LOCKUP!"); - client->printRefs(); - } -#endif } -// set preview/capture parameters - key/value pairs -status_t CameraService::Client::setParameters(const String8& params) -{ - LOGV("setParameters(%s)", params.string()); - - Mutex::Autolock lock(mLock); - status_t result = checkPid(); - if (result != NO_ERROR) return result; - - if (mHardware == 0) { - LOGE("mHardware is NULL, returning."); - return INVALID_OPERATION; - } - - CameraParameters p(params); - - return mHardware->setParameters(p); -} - -// get preview/capture parameters - key/value pairs -String8 CameraService::Client::getParameters() const -{ - Mutex::Autolock lock(mLock); - - if (mHardware == 0) { - LOGE("mHardware is NULL, returning."); - return String8(); - } - - String8 params(mHardware->getParameters().flatten()); - LOGV("getParameters(%s)", params.string()); - return params; -} - -status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) -{ - LOGV("sendCommand (pid %d)", getCallingPid()); - Mutex::Autolock lock(mLock); - status_t result = checkPid(); - if (result != NO_ERROR) return result; - - if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) { - // The orientation cannot be set during preview. - if (mHardware->previewEnabled()) { - return INVALID_OPERATION; - } - switch (arg1) { - case 0: - mOrientation = ISurface::BufferHeap::ROT_0; - break; - case 90: - mOrientation = ISurface::BufferHeap::ROT_90; - break; - case 180: - mOrientation = ISurface::BufferHeap::ROT_180; - break; - case 270: - mOrientation = ISurface::BufferHeap::ROT_270; - break; - default: - return BAD_VALUE; - } - return OK; - } - - if (mHardware == 0) { - LOGE("mHardware is NULL, returning."); - return INVALID_OPERATION; - } - - return mHardware->sendCommand(cmd, arg1, arg2); -} - -void CameraService::Client::copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client, - const sp<IMemoryHeap>& heap, size_t offset, size_t size) -{ - LOGV("copyFrameAndPostCopiedFrame"); +void CameraService::Client::copyFrameAndPostCopiedFrame( + const sp<ICameraClient>& client, const sp<IMemoryHeap>& heap, + size_t offset, size_t size) { + LOG2("copyFrameAndPostCopiedFrame"); // It is necessary to copy out of pmem before sending this to // the callback. For efficiency, reuse the same MemoryHeapBase // provided it's big enough. Don't allocate the memory or // perform the copy if there's no callback. - // hold the preview lock while we grab a reference to the preview buffer sp<MemoryHeapBase> previewBuffer; - { - Mutex::Autolock lock(mPreviewLock); - if (mPreviewBuffer == 0) { - mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); - } else if (size > mPreviewBuffer->virtualSize()) { - mPreviewBuffer.clear(); - mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); - } - if (mPreviewBuffer == 0) { - LOGE("failed to allocate space for preview buffer"); - return; - } - previewBuffer = mPreviewBuffer; + + if (mPreviewBuffer == 0) { + mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); + } else if (size > mPreviewBuffer->virtualSize()) { + mPreviewBuffer.clear(); + mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); + } + if (mPreviewBuffer == 0) { + LOGE("failed to allocate space for preview buffer"); + mLock.unlock(); + return; } - memcpy(previewBuffer->base(), - (uint8_t *)heap->base() + offset, size); + previewBuffer = mPreviewBuffer; + + memcpy(previewBuffer->base(), (uint8_t *)heap->base() + offset, size); sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size); if (frame == 0) { LOGE("failed to allocate space for frame callback"); + mLock.unlock(); return; } + + mLock.unlock(); client->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame); } +// ---------------------------------------------------------------------------- + static const int kDumpLockRetries = 50; static const int kDumpLockSleep = 60000; @@ -1308,8 +1227,7 @@ static bool tryLock(Mutex& mutex) return locked; } -status_t CameraService::dump(int fd, const Vector<String16>& args) -{ +status_t CameraService::dump(int fd, const Vector<String16>& args) { static const char* kDeadlockedString = "CameraService may be deadlocked\n"; const size_t SIZE = 256; @@ -1319,7 +1237,7 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) snprintf(buffer, SIZE, "Permission Denial: " "can't dump CameraService from pid=%d, uid=%d\n", getCallingPid(), - IPCThreadState::self()->getCallingUid()); + getCallingUid()); result.append(buffer); write(fd, result.string(), result.size()); } else { @@ -1330,89 +1248,39 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) write(fd, result.string(), result.size()); } - if (mClient != 0) { - sp<Client> currentClient = mClient.promote(); - sprintf(buffer, "Client (%p) PID: %d\n", - currentClient->getCameraClient()->asBinder().get(), - currentClient->mClientPid); + bool hasClient = false; + for (int i = 0; i < NUM_CAMERAS; i++) { + sp<Client> client = mClient[i].promote(); + if (client == 0) continue; + hasClient = true; + sprintf(buffer, "Client[%d] (%p) PID: %d\n", + i, + client->getCameraClient()->asBinder().get(), + client->mClientPid); result.append(buffer); write(fd, result.string(), result.size()); - currentClient->mHardware->dump(fd, args); - } else { + client->mHardware->dump(fd, args); + } + if (!hasClient) { result.append("No camera client yet.\n"); write(fd, result.string(), result.size()); } if (locked) mServiceLock.unlock(); - } - return NO_ERROR; -} - -status_t CameraService::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - // permission checks... - switch (code) { - case BnCameraService::CONNECT: - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int self_pid = getpid(); - if (pid != self_pid) { - // we're called from a different process, do the real check - if (!checkCallingPermission( - String16("android.permission.CAMERA"))) - { - const int uid = ipc->getCallingUid(); - LOGE("Permission Denial: " - "can't use the camera pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } - } - break; - } - - status_t err = BnCameraService::onTransact(code, data, reply, flags); - -#if DEBUG_HEAP_LEAKS - LOGV("+++ onTransact err %d code %d", err, code); - - if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { - // the 'service' command interrogates this binder for its name, and then supplies it - // even for the debugging commands. that means we need to check for it here, using - // ISurfaceComposer (since we delegated the INTERFACE_TRANSACTION handling to - // BnSurfaceComposer before falling through to this code). - - LOGV("+++ onTransact code %d", code); - - CHECK_INTERFACE(ICameraService, data, reply); - - switch(code) { - case 1000: - { - if (gWeakHeap != 0) { - sp<IMemoryHeap> h = gWeakHeap.promote(); - IMemoryHeap *p = gWeakHeap.unsafe_get(); - LOGV("CHECKING WEAK REFERENCE %p (%p)", h.get(), p); - if (h != 0) - h->printRefs(); - bool attempt_to_delete = data.readInt32() == 1; - if (attempt_to_delete) { - // NOT SAFE! - LOGV("DELETING WEAK REFERENCE %p (%p)", h.get(), p); - if (p) delete p; - } - return NO_ERROR; + // change logging level + int n = args.size(); + for (int i = 0; i + 1 < n; i++) { + if (args[i] == String16("-v")) { + String8 levelStr(args[i+1]); + int level = atoi(levelStr.string()); + sprintf(buffer, "Set Log Level to %d", level); + result.append(buffer); + setLogLevel(level); } } - break; - default: - break; - } } -#endif // DEBUG_HEAP_LEAKS - - return err; + return NO_ERROR; } }; // namespace android diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h index bc49b1d..86986ca 100644 --- a/camera/libcameraservice/CameraService.h +++ b/camera/libcameraservice/CameraService.h @@ -21,207 +21,171 @@ #include <camera/ICameraService.h> #include <camera/CameraHardwareInterface.h> -#include <camera/Camera.h> + +/* This needs to be increased if we can have more cameras */ +#define MAX_CAMERAS 2 namespace android { class MemoryHeapBase; class MediaPlayer; -// ---------------------------------------------------------------------------- - -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) - -// When enabled, this feature allows you to send an event to the CameraService -// so that you can cause all references to the heap object gWeakHeap, defined -// below, to be printed. You will also need to set DEBUG_REFS=1 and -// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp. You just have to -// set gWeakHeap to the appropriate heap you want to track. - -#define DEBUG_HEAP_LEAKS 0 - -// ---------------------------------------------------------------------------- - -class CameraService : public BnCameraService +class CameraService: public BnCameraService { class Client; - public: - static void instantiate(); + static void instantiate(); + + CameraService(); + virtual ~CameraService(); - // ICameraService interface - virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient); + virtual int32_t getNumberOfCameras(); + virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId); + virtual void removeClient(const sp<ICameraClient>& cameraClient); + virtual sp<Client> getClientById(int cameraId); - virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); - void removeClient(const sp<ICameraClient>& cameraClient); + enum sound_kind { + SOUND_SHUTTER = 0, + SOUND_RECORDING = 1, + NUM_SOUNDS + }; - virtual status_t onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + void loadSound(); + void playSound(sound_kind kind); + void releaseSound(); private: + Mutex mServiceLock; + wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock -// ---------------------------------------------------------------------------- + // atomics to record whether the hardware is allocated to some client. + volatile int32_t mBusy[MAX_CAMERAS]; + void setCameraBusy(int cameraId); + void setCameraFree(int cameraId); - class Client : public BnCamera { + // sounds + Mutex mSoundLock; + sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS]; + int mSoundRef; // reference count (release all MediaPlayer when 0) + class Client : public BnCamera + { public: + // ICamera interface (see ICamera for details) virtual void disconnect(); - - // connect new client with existing camera remote virtual status_t connect(const sp<ICameraClient>& client); - - // prevent other processes from using this ICamera interface virtual status_t lock(); - - // allow other processes to use this ICamera interface virtual status_t unlock(); - - // pass the buffered ISurface to the camera service virtual status_t setPreviewDisplay(const sp<ISurface>& surface); - - // set the preview callback flag to affect how the received frames from - // preview are handled. - virtual void setPreviewCallbackFlag(int callback_flag); - - // start preview mode, must call setPreviewDisplay first + virtual void setPreviewCallbackFlag(int flag); virtual status_t startPreview(); - - // stop preview mode virtual void stopPreview(); - - // get preview state virtual bool previewEnabled(); - - // start recording mode virtual status_t startRecording(); - - // stop recording mode virtual void stopRecording(); - - // get recording state virtual bool recordingEnabled(); - - // release a recording frame virtual void releaseRecordingFrame(const sp<IMemory>& mem); - - // auto focus virtual status_t autoFocus(); - - // cancel auto focus virtual status_t cancelAutoFocus(); - - // take a picture - returns an IMemory (ref-counted mmap) virtual status_t takePicture(); - - // set preview/capture parameters - key/value pairs virtual status_t setParameters(const String8& params); - - // get preview/capture parameters - key/value pairs virtual String8 getParameters() const; - - // send command to camera driver virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2); - - // our client... - const sp<ICameraClient>& getCameraClient() const { return mCameraClient; } - private: friend class CameraService; Client(const sp<CameraService>& cameraService, - const sp<ICameraClient>& cameraClient, - pid_t clientPid); - Client(); - virtual ~Client(); - - status_t checkPid(); - - static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user); - static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user); - static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, - const sp<IMemory>& dataPtr, void* user); + const sp<ICameraClient>& cameraClient, + int cameraId, + int clientPid); + ~Client(); - static sp<Client> getClientFromCookie(void* user); + // return our camera client + const sp<ICameraClient>& getCameraClient() { return mCameraClient; } - void handlePreviewData(const sp<IMemory>&); - void handleShutter(image_rect_type *image); - void handlePostview(const sp<IMemory>&); - void handleRawPicture(const sp<IMemory>&); - void handleCompressedPicture(const sp<IMemory>&); + // check whether the calling process matches mClientPid. + status_t checkPid() const; + status_t checkPidAndHardware() const; // also check mHardware != 0 - void copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client, - const sp<IMemoryHeap>& heap, size_t offset, size_t size); + // these are internal functions used to set up preview buffers + status_t registerPreviewBuffers(); + status_t setOverlay(); // camera operation mode enum camera_mode { CAMERA_PREVIEW_MODE = 0, // frame automatically released CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame() }; + // these are internal functions used for preview/recording status_t startCameraMode(camera_mode mode); status_t startPreviewMode(); status_t startRecordingMode(); - status_t setOverlay(); - status_t registerPreviewBuffers(); + + // these are static callback functions + static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user); + static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user); + static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user); + // convert client from cookie + static sp<Client> getClientFromCookie(void* user); + // handlers for messages + void handleShutter(image_rect_type *size); + void handlePreviewData(const sp<IMemory>& mem); + void handlePostview(const sp<IMemory>& mem); + void handleRawPicture(const sp<IMemory>& mem); + void handleCompressedPicture(const sp<IMemory>& mem); + void handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2); + void handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr); + void handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr); + + void copyFrameAndPostCopiedFrame( + const sp<ICameraClient>& client, + const sp<IMemoryHeap>& heap, + size_t offset, size_t size); + + // these are initialized in the constructor. + sp<CameraService> mCameraService; // immutable after constructor + sp<ICameraClient> mCameraClient; + int mCameraId; // immutable after constructor + pid_t mClientPid; + sp<CameraHardwareInterface> mHardware; // cleared after disconnect() + bool mUseOverlay; // immutable after constructor + sp<OverlayRef> mOverlayRef; + int mOverlayW; + int mOverlayH; + int mPreviewCallbackFlag; + int mOrientation; // Ensures atomicity among the public methods - mutable Mutex mLock; - - // mSurfaceLock synchronizes access to mSurface between - // setPreviewSurface() and postPreviewFrame(). Note that among - // the public methods, all accesses to mSurface are - // syncrhonized by mLock. However, postPreviewFrame() is called - // by the CameraHardwareInterface callback, and needs to - // access mSurface. It cannot hold mLock, however, because - // stopPreview() may be holding that lock while attempting - // to stop preview, and stopPreview itself will block waiting - // for a callback from CameraHardwareInterface. If this - // happens, it will cause a deadlock. - mutable Mutex mSurfaceLock; - mutable Condition mReady; - sp<CameraService> mCameraService; - sp<ISurface> mSurface; - int mPreviewCallbackFlag; - int mOrientation; - - sp<MediaPlayer> mMediaPlayerClick; - sp<MediaPlayer> mMediaPlayerBeep; - - // these are immutable once the object is created, - // they don't need to be protected by a lock - sp<ICameraClient> mCameraClient; - sp<CameraHardwareInterface> mHardware; - pid_t mClientPid; - bool mUseOverlay; - - sp<OverlayRef> mOverlayRef; - int mOverlayW; - int mOverlayH; - - mutable Mutex mPreviewLock; - sp<MemoryHeapBase> mPreviewBuffer; + mutable Mutex mLock; + sp<ISurface> mSurface; + + // If the user want us to return a copy of the preview frame (instead + // of the original one), we allocate mPreviewBuffer and reuse it if possible. + sp<MemoryHeapBase> mPreviewBuffer; + + // We need to avoid the deadlock when the incoming command thread and + // the CameraHardwareInterface callback thread both want to grab mLock. + // An extra flag is used to tell the callback thread that it should stop + // trying to deliver the callback messages if the client is not + // interested in it anymore. For example, if the client is calling + // stopPreview(), the preview frame messages do not need to be delivered + // anymore. + + // This function takes the same parameter as the enableMsgType() and + // disableMsgType() functions in CameraHardwareInterface. + void enableMsgType(int32_t msgType); + void disableMsgType(int32_t msgType); + volatile int32_t mMsgEnabled; + + // This function keeps trying to grab mLock, or give up if the message + // is found to be disabled. It returns true if mLock is grabbed. + bool lockIfMessageWanted(int32_t msgType); }; - -// ---------------------------------------------------------------------------- - - CameraService(); - virtual ~CameraService(); - - // We use a count for number of clients (shoule only be 0 or 1). - volatile int32_t mUsers; - virtual void incUsers(); - virtual void decUsers(); - - mutable Mutex mServiceLock; - wp<Client> mClient; - -#if DEBUG_HEAP_LEAKS - wp<IMemoryHeap> gWeakHeap; -#endif }; -// ---------------------------------------------------------------------------- - -}; // namespace android +} // namespace android #endif diff --git a/camera/libcameraservice/FakeCamera.cpp b/camera/libcameraservice/FakeCamera.cpp index 6749899..f3a6a67 100644 --- a/camera/libcameraservice/FakeCamera.cpp +++ b/camera/libcameraservice/FakeCamera.cpp @@ -198,10 +198,11 @@ static const int SHIFT2 = 16; static const int DELTA = kYb*(1 << SHIFT2); static const int GAMMA = kYr*(1 << SHIFT2); -int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16,uint8_t *yuv422,uint32_t *param,uint8_t *table[]) +int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16, uint8_t *yuv420, + uint32_t *param, uint8_t *table[]) { uint16_t *inputRGB = (uint16_t*)rgb16; - uint8_t *outYUV = yuv422; + uint8_t *outYUV = yuv420; int32_t width_dst = param[0]; int32_t height_dst = param[1]; int32_t pitch_dst = param[2]; @@ -260,12 +261,14 @@ uint32_t temp; tempY[0] = y0; tempY[1] = y1; - tempU[0] = u; - tempV[0] = v; - tempY += 2; - tempU += 2; - tempV += 2; + + if ((j&1) == 0) { + tempU[0] = u; + tempV[0] = v; + tempU += 2; + tempV += 2; + } } inputRGB += pitch_src; @@ -277,7 +280,7 @@ uint32_t temp; #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) -static void convert_rgb16_to_yuv422(uint8_t *rgb, uint8_t *yuv, int width, int height) +static void convert_rgb16_to_yuv420(uint8_t *rgb, uint8_t *yuv, int width, int height) { if (!tables_initialized) { initYtab(); @@ -326,7 +329,7 @@ void FakeCamera::setSize(int width, int height) mCheckY = 0; // This will cause it to be reallocated on the next call - // to getNextFrameAsYuv422(). + // to getNextFrameAsYuv420(). delete[] mTmpRgb16Buffer; mTmpRgb16Buffer = 0; } @@ -347,13 +350,13 @@ void FakeCamera::getNextFrameAsRgb565(uint16_t *buffer) mCounter++; } -void FakeCamera::getNextFrameAsYuv422(uint8_t *buffer) +void FakeCamera::getNextFrameAsYuv420(uint8_t *buffer) { if (mTmpRgb16Buffer == 0) mTmpRgb16Buffer = new uint16_t[mWidth * mHeight]; getNextFrameAsRgb565(mTmpRgb16Buffer); - convert_rgb16_to_yuv422((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight); + convert_rgb16_to_yuv420((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight); } void FakeCamera::drawSquare(uint16_t *dst, int x, int y, int size, int color, int shadow) diff --git a/camera/libcameraservice/FakeCamera.h b/camera/libcameraservice/FakeCamera.h index f7f8803..724de20 100644 --- a/camera/libcameraservice/FakeCamera.h +++ b/camera/libcameraservice/FakeCamera.h @@ -40,7 +40,7 @@ public: ~FakeCamera(); void setSize(int width, int height); - void getNextFrameAsYuv422(uint8_t *buffer); + void getNextFrameAsYuv420(uint8_t *buffer); // Write to the fd a string representing the current state. void dump(int fd) const; diff --git a/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/camera/tests/CameraServiceTest/CameraServiceTest.cpp index 9fc795b..41670af 100644 --- a/camera/tests/CameraServiceTest/CameraServiceTest.cpp +++ b/camera/tests/CameraServiceTest/CameraServiceTest.cpp @@ -38,7 +38,7 @@ void assert_fail(const char *file, int line, const char *func, const char *expr) INFO("assertion failed at file %s, line %d, function %s:", file, line, func); INFO("%s", expr); - exit(1); + abort(); } void assert_eq_fail(const char *file, int line, const char *func, @@ -46,7 +46,7 @@ void assert_eq_fail(const char *file, int line, const char *func, INFO("assertion failed at file %s, line %d, function %s:", file, line, func); INFO("(expected) %s != (actual) %d", expr, actual); - exit(1); + abort(); } #define ASSERT(e) \ @@ -155,7 +155,7 @@ public: virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2); virtual void dataCallback(int32_t msgType, const sp<IMemory>& data); virtual void dataCallbackTimestamp(nsecs_t timestamp, - int32_t msgType, const sp<IMemory>& data) {} + int32_t msgType, const sp<IMemory>& data); // new functions void clearStat(); @@ -176,6 +176,7 @@ private: DefaultKeyedVector<int32_t, int> mDataCount; DefaultKeyedVector<int32_t, int> mDataSize; bool test(OP op, int v1, int v2); + void assertTest(OP op, int v1, int v2); ICamera *mReleaser; }; @@ -199,22 +200,29 @@ bool MCameraClient::test(OP op, int v1, int v2) { return false; } +void MCameraClient::assertTest(OP op, int v1, int v2) { + if (!test(op, v1, v2)) { + LOGE("assertTest failed: op=%d, v1=%d, v2=%d", op, v1, v2); + ASSERT(0); + } +} + void MCameraClient::assertNotify(int32_t msgType, OP op, int count) { Mutex::Autolock _l(mLock); int v = mNotifyCount.valueFor(msgType); - ASSERT(test(op, v, count)); + assertTest(op, v, count); } void MCameraClient::assertData(int32_t msgType, OP op, int count) { Mutex::Autolock _l(mLock); int v = mDataCount.valueFor(msgType); - ASSERT(test(op, v, count)); + assertTest(op, v, count); } void MCameraClient::assertDataSize(int32_t msgType, OP op, int dataSize) { Mutex::Autolock _l(mLock); int v = mDataSize.valueFor(msgType); - ASSERT(test(op, v, dataSize)); + assertTest(op, v, dataSize); } void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) { @@ -250,6 +258,11 @@ void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) { } } +void MCameraClient::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, + const sp<IMemory>& data) { + dataCallback(msgType, data); +} + void MCameraClient::waitNotify(int32_t msgType, OP op, int count) { INFO("waitNotify: %d, %d, %d", msgType, op, count); Mutex::Autolock _l(mLock); @@ -348,10 +361,9 @@ void MSurface::waitUntil(int c0, int c1, int c2) { sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format, int32_t orientation) { - // We don't expect this to be called in current hardware. + // Not implemented. ASSERT(0); - sp<OverlayRef> dummy; - return dummy; + return NULL; } // @@ -395,38 +407,43 @@ sp<ICameraService> getCameraService() { return cs; } +int getNumberOfCameras() { + sp<ICameraService> cs = getCameraService(); + return cs->getNumberOfCameras(); +} + // // Various Connect Tests // -void testConnect() { +void testConnect(int cameraId) { INFO(__func__); sp<ICameraService> cs = getCameraService(); sp<MCameraClient> cc = new MCameraClient(); - sp<ICamera> c = cs->connect(cc); + sp<ICamera> c = cs->connect(cc, cameraId); ASSERT(c != 0); c->disconnect(); } -void testAllowConnectOnceOnly() { +void testAllowConnectOnceOnly(int cameraId) { INFO(__func__); sp<ICameraService> cs = getCameraService(); // Connect the first client. sp<MCameraClient> cc = new MCameraClient(); - sp<ICamera> c = cs->connect(cc); + sp<ICamera> c = cs->connect(cc, cameraId); ASSERT(c != 0); // Same client -- ok. - ASSERT(cs->connect(cc) != 0); + ASSERT(cs->connect(cc, cameraId) != 0); // Different client -- not ok. sp<MCameraClient> cc2 = new MCameraClient(); - ASSERT(cs->connect(cc2) == 0); + ASSERT(cs->connect(cc2, cameraId) == 0); c->disconnect(); } void testReconnectFailed() { INFO(__func__); sp<ICamera> c = interface_cast<ICamera>(getTempObject()); - sp<MCameraClient> cc2 = new MCameraClient(); - ASSERT(c->connect(cc2) != NO_ERROR); + sp<MCameraClient> cc = new MCameraClient(); + ASSERT(c->connect(cc) != NO_ERROR); } void testReconnectSuccess() { @@ -434,6 +451,7 @@ void testReconnectSuccess() { sp<ICamera> c = interface_cast<ICamera>(getTempObject()); sp<MCameraClient> cc = new MCameraClient(); ASSERT(c->connect(cc) == NO_ERROR); + c->disconnect(); } void testLockFailed() { @@ -453,6 +471,7 @@ void testLockSuccess() { INFO(__func__); sp<ICamera> c = interface_cast<ICamera>(getTempObject()); ASSERT(c->lock() == NO_ERROR); + c->disconnect(); } // @@ -499,11 +518,11 @@ void runInAnotherProcess(const char *tag) { } } -void testReconnect() { +void testReconnect(int cameraId) { INFO(__func__); sp<ICameraService> cs = getCameraService(); sp<MCameraClient> cc = new MCameraClient(); - sp<ICamera> c = cs->connect(cc); + sp<ICamera> c = cs->connect(cc, cameraId); ASSERT(c != 0); // Reconnect to the same client -- ok. ASSERT(c->connect(cc) == NO_ERROR); @@ -514,10 +533,10 @@ void testReconnect() { cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); } -void testLockUnlock() { +void testLockUnlock(int cameraId) { sp<ICameraService> cs = getCameraService(); sp<MCameraClient> cc = new MCameraClient(); - sp<ICamera> c = cs->connect(cc); + sp<ICamera> c = cs->connect(cc, cameraId); ASSERT(c != 0); // We can lock as many times as we want. ASSERT(c->lock() == NO_ERROR); @@ -530,16 +549,15 @@ void testLockUnlock() { runInAnotherProcess("testLockUnlockSuccess"); // Unlock then lock from a different process -- ok. runInAnotherProcess("testLockSuccess"); - c->disconnect(); clearTempObject(); } -void testReconnectFromAnotherProcess() { +void testReconnectFromAnotherProcess(int cameraId) { INFO(__func__); sp<ICameraService> cs = getCameraService(); sp<MCameraClient> cc = new MCameraClient(); - sp<ICamera> c = cs->connect(cc); + sp<ICamera> c = cs->connect(cc, cameraId); ASSERT(c != 0); // Reconnect from a different process -- not ok. putTempObject(c->asBinder()); @@ -547,7 +565,6 @@ void testReconnectFromAnotherProcess() { // Unlock then reconnect from a different process -- ok. ASSERT(c->unlock() == NO_ERROR); runInAnotherProcess("testReconnectSuccess"); - c->disconnect(); clearTempObject(); } @@ -560,10 +577,11 @@ static void flushCommands() { } // Run a test case -#define RUN(class_name) do { \ +#define RUN(class_name, cameraId) do { \ { \ INFO(#class_name); \ class_name instance; \ + instance.init(cameraId); \ instance.run(); \ } \ flushCommands(); \ @@ -571,19 +589,21 @@ static void flushCommands() { // Base test case after the the camera is connected. class AfterConnect { -protected: - sp<ICameraService> cs; - sp<MCameraClient> cc; - sp<ICamera> c; - - AfterConnect() { +public: + void init(int cameraId) { cs = getCameraService(); cc = new MCameraClient(); - c = cs->connect(cc); + c = cs->connect(cc, cameraId); ASSERT(c != 0); } +protected: + sp<ICameraService> cs; + sp<MCameraClient> cc; + sp<ICamera> c; + ~AfterConnect() { + c->disconnect(); c.clear(); cc.clear(); cs.clear(); @@ -612,19 +632,16 @@ public: surface->waitUntil(1, 10, 0); // needs 1 registerBuffers and 10 postBuffer surface->clearStat(); - c->disconnect(); - // TODO: CameraService crashes for this. Fix it. -#if 0 sp<MSurface> another_surface = new MSurface(); c->setPreviewDisplay(another_surface); // just to make sure unregisterBuffers // is called. surface->waitUntil(0, 0, 1); // needs unregisterBuffers -#endif + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); } }; -class TestStartPreviewWithoutDisplay : AfterConnect { +class TestStartPreviewWithoutDisplay : public AfterConnect { public: void run() { ASSERT(c->startPreview() == NO_ERROR); @@ -636,15 +653,17 @@ public: // Base test case after the the camera is connected and the preview is started. class AfterStartPreview : public AfterConnect { -protected: - sp<MSurface> surface; - - AfterStartPreview() { +public: + void init(int cameraId) { + AfterConnect::init(cameraId); surface = new MSurface(); ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); ASSERT(c->startPreview() == NO_ERROR); } +protected: + sp<MSurface> surface; + ~AfterStartPreview() { surface.clear(); } @@ -680,9 +699,6 @@ public: cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); c->stopPreview(); -#if 1 // TODO: It crashes if we don't have this. Fix it. - usleep(100000); -#endif c->disconnect(); cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); } @@ -697,7 +713,6 @@ public: cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1); cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); - usleep(100000); // 100ms } c->disconnect(); cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); @@ -712,32 +727,67 @@ public: } }; +static bool getNextSize(const char **ptrS, int *w, int *h) { + const char *s = *ptrS; + + // skip over ',' + if (*s == ',') s++; + + // remember start position in p + const char *p = s; + while (*s != '\0' && *s != 'x') { + s++; + } + if (*s == '\0') return false; + + // get the width + *w = atoi(p); + + // skip over 'x' + ASSERT(*s == 'x'); + p = s + 1; + while (*s != '\0' && *s != ',') { + s++; + } + + // get the height + *h = atoi(p); + *ptrS = s; + return true; +} + class TestPictureSize : public AfterStartPreview { public: void checkOnePicture(int w, int h) { - const float rate = 0.5; // byte per pixel limit + const float rate = 0.9; // byte per pixel limit int pixels = w * h; CameraParameters param(c->getParameters()); param.setPictureSize(w, h); + // disable thumbnail to get more accurate size. + param.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, 0); + param.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, 0); c->setParameters(param.flatten()); cc->clearStat(); ASSERT(c->takePicture() == NO_ERROR); cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); - cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2); + //cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2); cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::LT, int(pixels * rate)); cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::GT, 0); cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); - usleep(100000); // 100ms } void run() { - checkOnePicture(2048, 1536); - checkOnePicture(1600, 1200); - checkOnePicture(1024, 768); + CameraParameters param(c->getParameters()); + int w, h; + const char *s = param.get(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES); + while (getNextSize(&s, &w, &h)) { + LOGD("checking picture size %dx%d", w, h); + checkOnePicture(w, h); + } } }; @@ -749,6 +799,8 @@ public: // Try all flag combinations. for (int v = 0; v < 8; v++) { + LOGD("TestPreviewCallbackFlag: flag=%d", v); + usleep(100000); // sleep a while to clear the in-flight callbacks. cc->clearStat(); c->setPreviewCallbackFlag(v); ASSERT(c->previewEnabled() == false); @@ -781,6 +833,7 @@ public: ASSERT(c->recordingEnabled() == true); sleep(2); c->stopRecording(); + usleep(100000); // sleep a while to clear the in-flight callbacks. cc->setReleaser(NULL); cc->assertData(CAMERA_MSG_VIDEO_FRAME, MCameraClient::GE, 10); } @@ -806,9 +859,13 @@ public: } void run() { - checkOnePicture(480, 320); - checkOnePicture(352, 288); - checkOnePicture(176, 144); + CameraParameters param(c->getParameters()); + int w, h; + const char *s = param.get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES); + while (getNextSize(&s, &w, &h)) { + LOGD("checking preview size %dx%d", w, h); + checkOnePicture(w, h); + } } }; @@ -827,23 +884,30 @@ int main(int argc, char **argv) INFO("CameraServiceTest start"); gExecutable = argv[0]; runHolderService(); - - testConnect(); flushCommands(); - testAllowConnectOnceOnly(); flushCommands(); - testReconnect(); flushCommands(); - testLockUnlock(); flushCommands(); - testReconnectFromAnotherProcess(); flushCommands(); - - RUN(TestSetPreviewDisplay); - RUN(TestStartPreview); - RUN(TestStartPreviewWithoutDisplay); - RUN(TestAutoFocus); - RUN(TestStopPreview); - RUN(TestTakePicture); - RUN(TestTakeMultiplePictures); - RUN(TestGetParameters); - RUN(TestPictureSize); - RUN(TestPreviewCallbackFlag); - RUN(TestRecording); - RUN(TestPreviewSize); + int n = getNumberOfCameras(); + INFO("%d Cameras available", n); + + for (int id = 0; id < n; id++) { + INFO("Testing camera %d", id); + testConnect(id); flushCommands(); + testAllowConnectOnceOnly(id); flushCommands(); + testReconnect(id); flushCommands(); + testLockUnlock(id); flushCommands(); + testReconnectFromAnotherProcess(id); flushCommands(); + + RUN(TestSetPreviewDisplay, id); + RUN(TestStartPreview, id); + RUN(TestStartPreviewWithoutDisplay, id); + RUN(TestAutoFocus, id); + RUN(TestStopPreview, id); + RUN(TestTakePicture, id); + RUN(TestTakeMultiplePictures, id); + RUN(TestGetParameters, id); + RUN(TestPictureSize, id); + RUN(TestPreviewCallbackFlag, id); + RUN(TestRecording, id); + RUN(TestPreviewSize, id); + } + + INFO("CameraServiceTest finished"); } |