diff options
Diffstat (limited to 'camera/libcameraservice/CameraService.cpp')
-rw-r--r-- | camera/libcameraservice/CameraService.cpp | 757 |
1 files changed, 757 insertions, 0 deletions
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp new file mode 100644 index 0000000..5784c4b --- /dev/null +++ b/camera/libcameraservice/CameraService.cpp @@ -0,0 +1,757 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#define LOG_TAG "CameraService" +#include <utils/Log.h> + +#include <utils/IServiceManager.h> +#include <utils/IPCThreadState.h> +#include <utils/String16.h> +#include <utils/Errors.h> +#include <utils/MemoryBase.h> +#include <utils/MemoryHeapBase.h> +#include <ui/ICameraService.h> + +#include "CameraService.h" + +namespace android { + +extern "C" { +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <pthread.h> +} + +// 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. + +#define DEBUG_CLIENT_REFERENCES 0 + +#define PICTURE_TIMEOUT seconds(5) + +#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 + +#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE +static int debug_frame_cnt; +#endif + +// ---------------------------------------------------------------------------- + +void CameraService::instantiate() { + defaultServiceManager()->addService( + String16("media.camera"), new CameraService()); +} + +// ---------------------------------------------------------------------------- + +CameraService::CameraService() : + BnCameraService() +{ + LOGI("CameraService started: pid=%d", getpid()); +} + +CameraService::~CameraService() +{ + if (mClient != 0) { + LOGE("mClient was still connected in destructor!"); + } +} + +sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient) +{ + LOGD("Connect E from ICameraClient %p", cameraClient->asBinder().get()); + + Mutex::Autolock lock(mLock); + 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... + LOGD("Connect X same client is reconnecting..."); + return currentClient; + } else { + // it's another client... boot the previous one... + LOGD("new client connecting, booting the old one..."); + mClient.clear(); + } + } else { + // can't promote, the previous client has died... + LOGD("new client connecting, old reference was dangling..."); + mClient.clear(); + } + } + + // create a new Client object + sp<Client> client = new Client(this, cameraClient); + mClient = client; +#if DEBUG_CLIENT_REFERENCES + // Enable tracking for this object, and track increments and decrements of + // the refcount. + client->trackMe(true, true); +#endif + LOGD("Connect X"); + return client; +} + +void CameraService::removeClient(const sp<ICameraClient>& cameraClient) +{ + // declar this outside the lock to make absolutely sure the + // destructor won't be called with the lock held. + sp<Client> client; + + Mutex::Autolock lock(mLock); + + if (mClient == 0) { + // This happens when we have already disconnected. + LOGV("mClient is null."); + return; + } + + // Promote mClient. It should never fail because we're called from + // a binder call, so someone has to have a strong reference. + client = mClient.promote(); + if (client == 0) { + LOGW("can't get a strong reference on mClient!"); + mClient.clear(); + return; + } + + if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) { + // ugh! that's not our client!! + LOGW("removeClient() called, but mClient doesn't match!"); + } else { + // okay, good, forget about mClient + mClient.clear(); + } +} + +CameraService::Client::Client(const sp<CameraService>& cameraService, + const sp<ICameraClient>& cameraClient) : + mCameraService(cameraService), mCameraClient(cameraClient), mHardware(0) +{ + LOGD("Client E constructor"); + mHardware = openCameraHardware(); + mHasFrameCallback = false; + LOGD("Client X constructor"); +} + +#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() +{ + // spin down hardware + LOGD("Client E destructor"); + if (mSurface != 0) { +#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 + } + + disconnect(); + LOGD("Client X destructor"); +} + +void CameraService::Client::disconnect() +{ + LOGD("Client E disconnect"); + Mutex::Autolock lock(mLock); + mCameraService->removeClient(mCameraClient); + if (mHardware != 0) { + // Before destroying mHardware, we must make sure it's in the + // idle state. + mHardware->stopPreview(); + // Cancel all picture callbacks. + mHardware->cancelPicture(true, true, true); + // Release the hardware resources. + mHardware->release(); + } + mHardware.clear(); + LOGD("Client X disconnect"); +} + +// pass the buffered ISurface to the camera service +status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) +{ + LOGD("setPreviewDisplay(%p)", surface.get()); + Mutex::Autolock lock(mLock); + Mutex::Autolock surfaceLock(mSurfaceLock); + // asBinder() is safe on NULL (returns NULL) + if (surface->asBinder() != mSurface->asBinder()) { + if (mSurface != 0) { + LOGD("clearing old preview surface %p", mSurface.get()); + mSurface->unregisterBuffers(); + } + mSurface = surface; + } + return NO_ERROR; +} + +// tell the service whether to callback with each preview frame +void CameraService::Client::setHasFrameCallback(bool installed) +{ + Mutex::Autolock lock(mLock); + mHasFrameCallback = installed; + // If installed is false, mPreviewBuffer will be released in stopPreview(). +} + +// start preview mode, must call setPreviewDisplay first +status_t CameraService::Client::startPreview() +{ + LOGD("startPreview()"); + + /* we cannot call into mHardware with mLock held because + * mHardware has callbacks onto us which acquire this lock + */ + + Mutex::Autolock lock(mLock); + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + if (mSurface == 0) { + LOGE("setPreviewDisplay must be called before startPreview!"); + return INVALID_OPERATION; + } + + // XXX: This needs to be improved. remove all hardcoded stuff + + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + mSurface->unregisterBuffers(); + +#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE + debug_frame_cnt = 0; +#endif + + status_t ret = mHardware->startPreview(previewCallback, + mCameraService.get()); + if (ret == NO_ERROR) { + mSurface->registerBuffers(w,h,w,h, + PIXEL_FORMAT_YCbCr_420_SP, + mHardware->getPreviewHeap()); + } + else LOGE("mHardware->startPreview() failed with status %d\n", + ret); + + return ret; +} + +// stop preview mode +void CameraService::Client::stopPreview() +{ + LOGD("stopPreview()"); + + Mutex::Autolock lock(mLock); + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return; + } + + mHardware->stopPreview(); + LOGD("stopPreview(), hardware stopped OK"); + + if (mSurface != 0) { + mSurface->unregisterBuffers(); + } + mPreviewBuffer.clear(); +} + +// 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->mLock); + 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; +} + + +#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; + + LOGD("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; + } + + LOGD("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++; + } + LOGD("done writing %d bytes to file [%s] in %d passes\n", + size, fname, cnt); + ::close(fd); +} +#endif + +// preview callback - frame buffer update +void CameraService::Client::previewCallback(const sp<IMemory>& mem, void* user) +{ + sp<Client> client = getClientFromCookie(user); + if (client == 0) { + return; + } + +#if DEBUG_HEAP_LEAKS && 0 // debugging + if (gWeakHeap == NULL) { + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + if (gWeakHeap != heap) { + LOGD("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) { + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + dump_to_file("/data/preview.yuv", + (uint8_t *)heap->base() + offset, size); + } + } +#endif + + // The strong pointer guarantees the client will exist, but no lock is held. + client->postFrame(mem); + +#if DEBUG_CLIENT_REFERENCES + //**** if the client's refcount is 1, then we are about to destroy it here, + // which is bad--print all refcounts. + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (PREVIEW) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +// take a picture - image is returned in callback +status_t CameraService::Client::autoFocus() +{ + LOGV("autoFocus"); + + Mutex::Autolock lock(mLock); + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + return mHardware->autoFocus(autoFocusCallback, + mCameraService.get()); +} + +// take a picture - image is returned in callback +status_t CameraService::Client::takePicture() +{ + LOGD("takePicture"); + + Mutex::Autolock lock(mLock); + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + if (mSurface != NULL) + mSurface->unregisterBuffers(); + + return mHardware->takePicture(shutterCallback, + yuvPictureCallback, + jpegPictureCallback, + mCameraService.get()); +} + +// picture callback - snapshot taken +void CameraService::Client::shutterCallback(void *user) +{ + sp<Client> client = getClientFromCookie(user); + if (client == 0) { + return; + } + + client->postShutter(); +} + +// picture callback - raw image ready +void CameraService::Client::yuvPictureCallback(const sp<IMemory>& mem, + void *user) +{ + sp<Client> client = getClientFromCookie(user); + if (client == 0) { + return; + } + if (mem == NULL) { + client->postRaw(NULL); + client->postError(UNKNOWN_ERROR); + return; + } + + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); +#if DEBUG_HEAP_LEAKS && 0 // debugging + gWeakHeap = heap; // debugging +#endif + + //LOGV("yuvPictureCallback(%d, %d, %p)", offset, size, user); +#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. + int w, h; + CameraParameters params(client->mHardware->getParameters()); + params.getPictureSize(&w, &h); + +// Mutex::Autolock clientLock(client->mLock); + if (client->mSurface != 0) { + client->mSurface->unregisterBuffers(); + client->mSurface->registerBuffers(w,h,w,h, + PIXEL_FORMAT_YCbCr_420_SP, heap); + client->mSurface->postBuffer(offset); + } + + client->postRaw(mem); + +#if DEBUG_CLIENT_REFERENCES + //**** if the client's refcount is 1, then we are about to destroy it here, + // which is bad--print all refcounts. + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (RAW) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +// picture callback - jpeg ready +void CameraService::Client::jpegPictureCallback(const sp<IMemory>& mem, void *user) +{ + sp<Client> client = getClientFromCookie(user); + if (client == 0) { + return; + } + if (mem == NULL) { + client->postJpeg(NULL); + client->postError(UNKNOWN_ERROR); + return; + } + + /** We absolutely CANNOT call into user code with a lock held **/ + +#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 + + client->postJpeg(mem); + +#if DEBUG_CLIENT_REFERENCES + //**** if the client's refcount is 1, then we are about to destroy it here, + // which is bad--print all refcounts. + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (JPEG) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +void CameraService::Client::autoFocusCallback(bool focused, void *user) +{ + LOGV("autoFocusCallback"); + + sp<Client> client = getClientFromCookie(user); + if (client == 0) { + return; + } + + client->postAutoFocus(focused); + +#if DEBUG_CLIENT_REFERENCES + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (AUTOFOCUS) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +// set preview/capture parameters - key/value pairs +status_t CameraService::Client::setParameters(const String8& params) +{ + LOGD("setParameters(%s)", params.string()); + + Mutex::Autolock lock(mLock); + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + CameraParameters p(params); + mHardware->setParameters(p); + return NO_ERROR; +} + +// get preview/capture parameters - key/value pairs +String8 CameraService::Client::getParameters() const +{ + LOGD("getParameters"); + + Mutex::Autolock lock(mLock); + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return String8(); + } + + return mHardware->getParameters().flatten(); +} + +void CameraService::Client::postAutoFocus(bool focused) +{ + LOGV("postAutoFocus"); + mCameraClient->autoFocusCallback(focused); +} + +void CameraService::Client::postShutter() +{ + mCameraClient->shutterCallback(); +} + +void CameraService::Client::postRaw(const sp<IMemory>& mem) +{ + LOGD("postRaw"); + mCameraClient->rawCallback(mem); +} + +void CameraService::Client::postJpeg(const sp<IMemory>& mem) +{ + LOGD("postJpeg"); + mCameraClient->jpegCallback(mem); +} + +void CameraService::Client::postFrame(const sp<IMemory>& mem) +{ + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + + sp<MemoryBase> frame; + + { + Mutex::Autolock surfaceLock(mSurfaceLock); + if (mSurface != NULL) + mSurface->postBuffer(offset); + } + + // 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. + if (mHasFrameCallback) { + if (mPreviewBuffer == 0) { + mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); + } else if (size > mPreviewBuffer->virtualSize()) { + mPreviewBuffer.clear(); + mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); + } + memcpy(mPreviewBuffer->base(), (uint8_t *)heap->base() + offset, size); + frame = new MemoryBase(mPreviewBuffer, 0, size); + } + + // Do not hold the client lock while calling back. + if (frame != 0) { + mCameraClient->frameCallback(frame); + } +} + +void CameraService::Client::postError(status_t error) { + mCameraClient->errorCallback(error); +} + +status_t CameraService::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump CameraService from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + } else { + AutoMutex lock(&mLock); + if (mClient != 0) { + sp<Client> currentClient = mClient.promote(); + currentClient->mHardware->dump(fd, args); + } else { + result.append("No camera client yet.\n"); + write(fd, result.string(), result.size()); + } + } + return NO_ERROR; +} + + +#if DEBUG_HEAP_LEAKS + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +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); + + LOGD("+++ 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). + + LOGD("+++ 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(); + LOGD("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! + LOGD("DELETING WEAK REFERENCE %p (%p)", h.get(), p); + if (p) delete p; + } + return NO_ERROR; + } + } + break; + default: + break; + } + } + return err; +} + +#endif // DEBUG_HEAP_LEAKS + +}; // namespace android + + |