diff options
40 files changed, 1925 insertions, 1681 deletions
diff --git a/camera/libcameraservice/Android.mk b/camera/libcameraservice/Android.mk index df5c166b27..a0d6ee15de 100644 --- a/camera/libcameraservice/Android.mk +++ b/camera/libcameraservice/Android.mk @@ -1,18 +1,21 @@ 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 don't want to use the hardware camera. -ifeq ($(USE_CAMERA_STUB),) -USE_CAMERA_STUB:=false +# force these builds to use camera stub only ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),) -USE_CAMERA_STUB:=true -endif #libcamerastub + USE_CAMERA_STUB:=true endif ifeq ($(USE_CAMERA_STUB),true) + INCLUDE_CAMERA_STUB:=true + INCLUDE_CAMERA_HARDWARE:=false +else + INCLUDE_CAMERA_STUB:=true # set this to true temporarily for testing + INCLUDE_CAMERA_HARDWARE:=true +endif + +ifeq ($(INCLUDE_CAMERA_STUB),true) # # libcamerastub # @@ -32,7 +35,7 @@ endif LOCAL_SHARED_LIBRARIES:= libui include $(BUILD_STATIC_LIBRARY) -endif # USE_CAMERA_STUB +endif # INCLUDE_CAMERA_STUB # # libcameraservice @@ -54,18 +57,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) +ifeq ($(INCLUDE_CAMERA_STUB), true) LOCAL_STATIC_LIBRARIES += libcamerastub -LOCAL_CFLAGS += -include CameraHardwareStub.h -else +LOCAL_CFLAGS += -DINCLUDE_CAMERA_STUB +endif + +ifeq ($(INCLUDE_CAMERA_HARDWARE),true) +LOCAL_CFLAGS += -DINCLUDE_CAMERA_HARDWARE LOCAL_SHARED_LIBRARIES += libcamera endif include $(BUILD_SHARED_LIBRARY) - diff --git a/camera/libcameraservice/CameraHardwareStub.cpp b/camera/libcameraservice/CameraHardwareStub.cpp index 8b663898a0..fda48e8c8c 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 957813a4a4..d194f3c4ed 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 00bd54eb59..4f684b7ba3 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -17,383 +17,448 @@ */ #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 INCLUDE_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(INCLUDE_CAMERA_HARDWARE) && defined(INCLUDE_CAMERA_STUB) + #define NUM_CAMERAS 2 +#elif defined(INCLUDE_CAMERA_HARDWARE) || defined(INCLUDE_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 INCLUDE_CAMERA_HARDWARE + &openCameraHardware, +#endif +#ifdef INCLUDE_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) 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; } @@ -401,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. @@ -927,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, @@ -936,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); } } @@ -977,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; } @@ -985,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) { @@ -1047,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; @@ -1307,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; @@ -1318,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 { @@ -1329,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 bc49b1dbd5..86986cab82 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 6749899827..f3a6a67ee8 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 f7f880328a..724de207f8 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 9fc795b3e1..41670af52a 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"); } diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h index 9b5a1e0f53..2504d39992 100644 --- a/include/private/surfaceflinger/SharedBufferStack.h +++ b/include/private/surfaceflinger/SharedBufferStack.h @@ -58,7 +58,7 @@ namespace android { // When changing these values, the COMPILE_TIME_ASSERT at the end of this // file need to be updated. const unsigned int NUM_LAYERS_MAX = 31; -const unsigned int NUM_BUFFER_MAX = 4; +const unsigned int NUM_BUFFER_MAX = 16; const unsigned int NUM_DISPLAY_MAX = 4; // ---------------------------------------------------------------------------- @@ -69,7 +69,11 @@ class SharedClient; // ---------------------------------------------------------------------------- -// should be 128 bytes (32 longs) +// 4 * (11 + 7 + (1 + 2*NUM_RECT_MAX) * NUM_BUFFER_MAX) * NUM_LAYERS_MAX +// 4 * (11 + 7 + (1 + 2*7)*16) * 31 +// 1032 * 31 +// = ~27 KiB (31992) + class SharedBufferStack { friend class SharedClient; @@ -78,21 +82,31 @@ class SharedBufferStack friend class SharedBufferServer; public: - struct FlatRegion { // 12 bytes - static const unsigned int NUM_RECT_MAX = 1; - uint32_t count; - uint16_t rects[4*NUM_RECT_MAX]; - }; - struct Statistics { // 4 longs typedef int32_t usecs_t; usecs_t totalTime; usecs_t reserved[3]; }; + + struct SmallRect { + uint16_t l, t, r, b; + }; + + struct FlatRegion { // 52 bytes = 4 * (1 + 2*N) + static const unsigned int NUM_RECT_MAX = 6; + uint32_t count; + SmallRect rects[NUM_RECT_MAX]; + }; + + struct BufferData { + FlatRegion dirtyRegion; + SmallRect crop; + }; SharedBufferStack(); void init(int32_t identity); status_t setDirtyRegion(int buffer, const Region& reg); + status_t setCrop(int buffer, const Rect& reg); Region getDirtyRegion(int buffer) const; // these attributes are part of the conditions/updates @@ -104,11 +118,13 @@ public: // not part of the conditions volatile int32_t reallocMask; + volatile int8_t index[NUM_BUFFER_MAX]; int32_t identity; // surface's identity (const) - int32_t reserved32[9]; + int32_t reserved32[2]; Statistics stats; - FlatRegion dirtyRegion[NUM_BUFFER_MAX]; // 12*4=48 bytes + int32_t reserved; + BufferData buffers[NUM_BUFFER_MAX]; // 960 bytes }; // ---------------------------------------------------------------------------- @@ -152,6 +168,7 @@ protected: SharedBufferStack* const mSharedStack; const int mNumBuffers; const int mIdentity; + int32_t computeTail() const; friend struct Update; friend struct QueueUpdate; @@ -160,61 +177,22 @@ protected: SharedBufferStack& stack; inline ConditionBase(SharedBufferBase* sbc) : stack(*sbc->mSharedStack) { } + virtual ~ConditionBase() { }; + virtual bool operator()() const = 0; + virtual const char* name() const = 0; }; + status_t waitForCondition(const ConditionBase& condition); struct UpdateBase { SharedBufferStack& stack; inline UpdateBase(SharedBufferBase* sbb) : stack(*sbb->mSharedStack) { } }; - - template <typename T> - status_t waitForCondition(T condition); - template <typename T> status_t updateCondition(T update); }; template <typename T> -status_t SharedBufferBase::waitForCondition(T condition) -{ - const SharedBufferStack& stack( *mSharedStack ); - SharedClient& client( *mSharedClient ); - const nsecs_t TIMEOUT = s2ns(1); - Mutex::Autolock _l(client.lock); - while ((condition()==false) && - (stack.identity == mIdentity) && - (stack.status == NO_ERROR)) - { - status_t err = client.cv.waitRelative(client.lock, TIMEOUT); - - // handle errors and timeouts - if (CC_UNLIKELY(err != NO_ERROR)) { - if (err == TIMED_OUT) { - if (condition()) { - LOGE("waitForCondition(%s) timed out (identity=%d), " - "but condition is true! We recovered but it " - "shouldn't happen." , T::name(), - stack.identity); - break; - } else { - LOGW("waitForCondition(%s) timed out " - "(identity=%d, status=%d). " - "CPU may be pegged. trying again.", T::name(), - stack.identity, stack.status); - } - } else { - LOGE("waitForCondition(%s) error (%s) ", - T::name(), strerror(-err)); - return err; - } - } - } - return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status; -} - - -template <typename T> status_t SharedBufferBase::updateCondition(T update) { SharedClient& client( *mSharedClient ); Mutex::Autolock _l(client.lock); @@ -238,13 +216,12 @@ public: status_t queue(int buf); bool needNewBuffer(int buffer) const; status_t setDirtyRegion(int buffer, const Region& reg); + status_t setCrop(int buffer, const Rect& reg); private: friend struct Condition; friend struct DequeueCondition; friend struct LockCondition; - - int32_t computeTail() const; struct QueueUpdate : public UpdateBase { inline QueueUpdate(SharedBufferBase* sbb); @@ -260,18 +237,20 @@ private: struct DequeueCondition : public ConditionBase { inline DequeueCondition(SharedBufferClient* sbc); - inline bool operator()(); - static inline const char* name() { return "DequeueCondition"; } + inline bool operator()() const; + inline const char* name() const { return "DequeueCondition"; } }; struct LockCondition : public ConditionBase { int buf; inline LockCondition(SharedBufferClient* sbc, int buf); - inline bool operator()(); - static inline const char* name() { return "LockCondition"; } + inline bool operator()() const; + inline const char* name() const { return "LockCondition"; } }; int32_t tail; + int32_t undoDequeueTail; + int32_t queued_head; // statistics... nsecs_t mDequeueTime[NUM_BUFFER_MAX]; }; @@ -318,8 +297,8 @@ private: struct ReallocateCondition : public ConditionBase { int buf; inline ReallocateCondition(SharedBufferBase* sbb, int buf); - inline bool operator()(); - static inline const char* name() { return "ReallocateCondition"; } + inline bool operator()() const; + inline const char* name() const { return "ReallocateCondition"; } }; }; @@ -349,8 +328,7 @@ struct surface_flinger_cblk_t // 4KB max // --------------------------------------------------------------------------- -COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 4096) -COMPILE_TIME_ASSERT(sizeof(SharedBufferStack) == 128) +COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 32768) COMPILE_TIME_ASSERT(sizeof(surface_flinger_cblk_t) <= 4096) // --------------------------------------------------------------------------- diff --git a/include/surfaceflinger/ISurfaceFlingerClient.h b/include/surfaceflinger/ISurfaceFlingerClient.h index d257645b65..c96432feb0 100644 --- a/include/surfaceflinger/ISurfaceFlingerClient.h +++ b/include/surfaceflinger/ISurfaceFlingerClient.h @@ -59,6 +59,9 @@ public: virtual sp<IMemoryHeap> getControlBlock() const = 0; + /* + * Requires ACCESS_SURFACE_FLINGER permission + */ virtual sp<ISurface> createSurface( surface_data_t* data, int pid, const String8& name, @@ -68,8 +71,14 @@ public: PixelFormat format, uint32_t flags) = 0; + /* + * Requires ACCESS_SURFACE_FLINGER permission + */ virtual status_t destroySurface(SurfaceID sid) = 0; + /* + * Requires ACCESS_SURFACE_FLINGER permission + */ virtual status_t setState(int32_t count, const layer_state_t* states) = 0; }; diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h index 0279d84b64..7ab3a00144 100644 --- a/include/surfaceflinger/Surface.h +++ b/include/surfaceflinger/Surface.h @@ -30,6 +30,8 @@ #include <surfaceflinger/ISurface.h> #include <surfaceflinger/ISurfaceFlingerClient.h> +#define ANDROID_VIEW_SURFACE_JNI_ID "mNativeSurface" + namespace android { // --------------------------------------------------------------------------- @@ -163,38 +165,36 @@ public: // setSwapRectangle() is intended to be used by GL ES clients void setSwapRectangle(const Rect& r); -private: - // can't be copied - Surface& operator = (Surface& rhs); - Surface(const Surface& rhs); - - Surface(const sp<SurfaceControl>& control); - void init(); - ~Surface(); - - friend class SurfaceComposerClient; - friend class SurfaceControl; - +private: + /* + * Android frameworks friends + * (eventually this should go away and be replaced by proper APIs) + */ // camera and camcorder need access to the ISurface binder interface for preview friend class Camera; friend class MediaRecorder; - // mediaplayer needs access to ISurface for display + // MediaPlayer needs access to ISurface for display friend class MediaPlayer; friend class IOMX; // this is just to be able to write some unit tests friend class Test; - sp<SurfaceComposerClient> getClient() const; - sp<ISurface> getISurface() const; +private: + friend class SurfaceComposerClient; + friend class SurfaceControl; - status_t getBufferLocked(int index, int usage); - - status_t validate() const; + // can't be copied + Surface& operator = (Surface& rhs); + Surface(const Surface& rhs); - inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; } - inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; } - + Surface(const sp<SurfaceControl>& control); + ~Surface(); + + + /* + * android_native_window_t hooks + */ static int setSwapInterval(android_native_window_t* window, int interval); static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer); static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer); @@ -208,15 +208,29 @@ private: int query(int what, int* value); int perform(int operation, va_list args); - status_t dequeueBuffer(sp<GraphicBuffer>* buffer); - void dispatch_setUsage(va_list args); int dispatch_connect(va_list args); int dispatch_disconnect(va_list args); + int dispatch_crop(va_list args); void setUsage(uint32_t reqUsage); int connect(int api); int disconnect(int api); + int crop(Rect const* rect); + + /* + * private stuff... + */ + void init(); + status_t validate() const; + sp<SurfaceComposerClient> getClient() const; + sp<ISurface> getISurface() const; + + inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; } + inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; } + + status_t getBufferLocked(int index, int usage); + int getBufferIndex(const sp<GraphicBuffer>& buffer) const; uint32_t getUsage() const; int getConnectedApi() const; @@ -235,6 +249,7 @@ private: Rect mSwapRectangle; uint32_t mUsage; int mConnected; + Rect mNextBufferCrop; // protected by mSurfaceLock. These are also used from lock/unlock // but in that case, they must be called form the same thread. @@ -245,7 +260,7 @@ private: sp<GraphicBuffer> mLockedBuffer; sp<GraphicBuffer> mPostedBuffer; mutable Region mOldDirtyRegion; - bool mNeedFullUpdate; + bool mReserved; // query() must be called from dequeueBuffer() thread uint32_t mWidth; diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h index 741d763561..54b8236e2a 100644 --- a/include/ui/GraphicBufferAllocator.h +++ b/include/ui/GraphicBufferAllocator.h @@ -73,9 +73,9 @@ private: struct alloc_rec_t { uint32_t w; uint32_t h; + uint32_t s; PixelFormat format; uint32_t usage; - void* vaddr; size_t size; }; diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h index 773fd9301f..471c3c70cb 100644 --- a/include/ui/egl/android_natives.h +++ b/include/ui/egl/android_natives.h @@ -41,6 +41,14 @@ extern "C" { struct android_native_buffer_t; +typedef struct android_native_rect_t +{ + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +} android_native_rect_t; + // --------------------------------------------------------------------------- typedef struct android_native_base_t @@ -63,15 +71,16 @@ typedef struct android_native_base_t /* attributes queriable with query() */ enum { NATIVE_WINDOW_WIDTH = 0, - NATIVE_WINDOW_HEIGHT = 1, - NATIVE_WINDOW_FORMAT = 2, + NATIVE_WINDOW_HEIGHT, + NATIVE_WINDOW_FORMAT, }; /* valid operations for the (*perform)() hook */ enum { NATIVE_WINDOW_SET_USAGE = 0, - NATIVE_WINDOW_CONNECT = 1, - NATIVE_WINDOW_DISCONNECT = 2 + NATIVE_WINDOW_CONNECT, + NATIVE_WINDOW_DISCONNECT, + NATIVE_WINDOW_SET_CROP, }; /* parameter for NATIVE_WINDOW_[DIS]CONNECT */ @@ -125,7 +134,7 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*dequeueBuffer)(struct android_native_window_t* window, + int (*dequeueBuffer)(struct android_native_window_t* window, struct android_native_buffer_t** buffer); /* @@ -171,6 +180,7 @@ typedef struct android_native_window_t * NATIVE_WINDOW_SET_USAGE * NATIVE_WINDOW_CONNECT * NATIVE_WINDOW_DISCONNECT + * NATIVE_WINDOW_SET_CROP * */ @@ -221,6 +231,24 @@ static inline int native_window_disconnect( return window->perform(window, NATIVE_WINDOW_DISCONNECT, api); } +/* + * native_window_set_crop(..., crop) sets which region of the next queued + * buffers needs to be considered. + * A buffer's crop region is scaled to match the surface's size. + * + * The specified crop region applies to all buffers queued after it is called. + * + * if 'crop' is NULL, subsequently queued buffers won't be cropped. + * + * An error is returned if for instance the crop region is invalid, + * out of the buffer's bound or if the window is invalid. + */ +static inline int native_window_set_crop( + android_native_window_t* window, + android_native_rect_t const * crop) +{ + return window->perform(window, NATIVE_WINDOW_SET_CROP, crop); +} // --------------------------------------------------------------------------- diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index b701ce74d6..c7d9ff1dd6 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -933,6 +933,7 @@ struct ResTable_config SCREENSIZE_SMALL = 0x01, SCREENSIZE_NORMAL = 0x02, SCREENSIZE_LARGE = 0x03, + SCREENSIZE_XLARGE = 0x04, // screenLayout bits for wide/long screen variation. MASK_SCREENLONG = 0x30, @@ -1208,7 +1209,28 @@ struct ResTable_config if (screenLayout || o.screenLayout) { if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 && (requested->screenLayout & MASK_SCREENSIZE)) { - return (screenLayout & MASK_SCREENSIZE); + // A little backwards compatibility here: undefined is + // considered equivalent to normal. But only if the + // requested size is at least normal; otherwise, small + // is better than the default. + int mySL = (screenLayout & MASK_SCREENSIZE); + int oSL = (o.screenLayout & MASK_SCREENSIZE); + int fixedMySL = mySL; + int fixedOSL = oSL; + if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) { + if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL; + if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL; + } + // For screen size, the best match is the one that is + // closest to the requested screen size, but not over + // (the not over part is dealt with in match() below). + if (fixedMySL == fixedOSL) { + // If the two are the same, but 'this' is actually + // undefined, then the other is really a better match. + if (mySL == 0) return false; + return true; + } + return fixedMySL >= fixedOSL; } if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0 && (requested->screenLayout & MASK_SCREENLONG)) { @@ -1370,8 +1392,11 @@ struct ResTable_config if (screenConfig != 0) { const int screenSize = screenLayout&MASK_SCREENSIZE; const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE; - if (setScreenSize != 0 && screenSize != 0 - && screenSize != setScreenSize) { + // Any screen sizes for larger screens than the setting do not + // match. + if ((setScreenSize != 0 && screenSize != 0 + && screenSize > setScreenSize) || + (setScreenSize == 0 && screenSize != 0)) { return false; } diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index ea68352828..d979f00c3b 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -266,9 +266,6 @@ void DisplayHardware::init(uint32_t dpy) if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) { mFlags |= NPOT_EXTENSION; } - if (strstr(gl_extensions, "GL_OES_draw_texture")) { - mFlags |= DRAW_TEXTURE_EXTENSION; - } #ifdef EGL_ANDROID_image_native_buffer if (strstr( gl_extensions, "GL_OES_EGL_image") && (strstr(egl_extensions, "EGL_KHR_image_base") || diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h index df046af95a..897a6edd8b 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -46,7 +46,6 @@ public: DIRECT_TEXTURE = 0x00000002, COPY_BITS_EXTENSION = 0x00000008, NPOT_EXTENSION = 0x00000100, - DRAW_TEXTURE_EXTENSION = 0x00000200, BUFFER_PRESERVED = 0x00010000, PARTIAL_UPDATES = 0x00020000, // video driver feature SLOW_CONFIG = 0x00040000, // software diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index ce7e9aa3f9..e6658fa7ce 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -47,11 +47,6 @@ template <typename T> inline T min(T a, T b) { // --------------------------------------------------------------------------- -const uint32_t Layer::typeInfo = LayerBaseClient::typeInfo | 4; -const char* const Layer::typeID = "Layer"; - -// --------------------------------------------------------------------------- - Layer::Layer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& c, int32_t i) : LayerBaseClient(flinger, display, c, i), @@ -559,9 +554,15 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) mFlinger->signalEvent(); } - if (!mPostedDirtyRegion.isEmpty()) { - reloadTexture( mPostedDirtyRegion ); - } + /* a buffer was posted, so we need to call reloadTexture(), which + * will update our internal data structures (eg: EGLImageKHR or + * texture names). we need to do this even if mPostedDirtyRegion is + * empty -- it's orthogonal to the fact that a new buffer was posted, + * for instance, a degenerate case could be that the user did an empty + * update but repainted the buffer with appropriate content (after a + * resize for instance). + */ + reloadTexture( mPostedDirtyRegion ); } void Layer::unlockPageFlip( @@ -585,7 +586,7 @@ void Layer::unlockPageFlip( } if (visibleRegionScreen.isEmpty()) { // an invisible layer should not hold a freeze-lock - // (because it may never be updated and thereore never release it) + // (because it may never be updated and therefore never release it) mFreezeLock.clear(); } } @@ -598,6 +599,38 @@ void Layer::finishPageFlip() this, mFrontBufferIndex); } + +void Layer::dump(String8& result, char* buffer, size_t SIZE) const +{ + LayerBaseClient::dump(result, buffer, SIZE); + + SharedBufferStack::Statistics stats = lcblk->getStats(); + result.append( lcblk->dump(" ") ); + sp<const GraphicBuffer> buf0(getBuffer(0)); + sp<const GraphicBuffer> buf1(getBuffer(1)); + uint32_t w0=0, h0=0, s0=0; + uint32_t w1=0, h1=0, s1=0; + if (buf0 != 0) { + w0 = buf0->getWidth(); + h0 = buf0->getHeight(); + s0 = buf0->getStride(); + } + if (buf1 != 0) { + w1 = buf1->getWidth(); + h1 = buf1->getHeight(); + s1 = buf1->getStride(); + } + snprintf(buffer, SIZE, + " " + "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u]," + " freezeLock=%p, dq-q-time=%u us\n", + pixelFormat(), + w0, h0, s0, w1, h1, s1, + getFreezeLock().get(), stats.totalTime); + + result.append(buffer); +} + // --------------------------------------------------------------------------- Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger, diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h index 743afb4c47..98e30d7597 100644 --- a/libs/surfaceflinger/Layer.h +++ b/libs/surfaceflinger/Layer.h @@ -46,11 +46,6 @@ const size_t NUM_BUFFERS = 2; class Layer : public LayerBaseClient { public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - Layer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i); @@ -73,7 +68,7 @@ public: virtual status_t ditch(); // only for debugging - inline sp<GraphicBuffer> getBuffer(int i) { return mBuffers[i]; } + inline sp<GraphicBuffer> getBuffer(int i) const { return mBuffers[i]; } // only for debugging inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } // only for debugging @@ -81,6 +76,11 @@ public: // only for debugging inline int getFrontBufferIndex() const { return mFrontBufferIndex; } + virtual const char* getTypeId() const { return "Layer"; } + +protected: + virtual void dump(String8& result, char* scratch, size_t size) const; + private: inline sp<GraphicBuffer> getFrontBufferLocked() { return mBuffers[mFrontBufferIndex]; diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp index a8b735ef6a..48b0e47a83 100644 --- a/libs/surfaceflinger/LayerBase.cpp +++ b/libs/surfaceflinger/LayerBase.cpp @@ -38,14 +38,6 @@ namespace android { // --------------------------------------------------------------------------- -const uint32_t LayerBase::typeInfo = 1; -const char* const LayerBase::typeID = "LayerBase"; - -const uint32_t LayerBaseClient::typeInfo = LayerBase::typeInfo | 2; -const char* const LayerBaseClient::typeID = "LayerBaseClient"; - -// --------------------------------------------------------------------------- - LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) : dpy(display), contentDirty(false), mFlinger(flinger), @@ -54,7 +46,7 @@ LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) mOrientation(0), mLeft(0), mTop(0), mTransactionFlags(0), - mPremultipliedAlpha(true), mDebug(false), + mPremultipliedAlpha(true), mName("unnamed"), mDebug(false), mInvalidate(0) { const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); @@ -374,7 +366,7 @@ void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red, Region::const_iterator it = clip.begin(); Region::const_iterator const end = clip.end(); glEnable(GL_SCISSOR_TEST); - glVertexPointer(2, GL_FIXED, 0, mVertices); + glVertexPointer(2, GL_FLOAT, 0, mVertices); while (it != end) { const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); @@ -418,14 +410,14 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const env = GL_REPLACE; src = GL_SRC_ALPHA; } - const GGLfixed alpha = (s.alpha << 16)/255; - glColor4x(alpha, alpha, alpha, alpha); + const GLfloat alpha = s.alpha * (1.0f/255.0f); + glColor4f(alpha, alpha, alpha, alpha); glEnable(GL_BLEND); glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env); } else { glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glColor4x(0x10000, 0x10000, 0x10000, 0x10000); + glColor4f(1, 1, 1, 1); if (needsBlending()) { GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; glEnable(GL_BLEND); @@ -437,13 +429,11 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const Region::const_iterator it = clip.begin(); Region::const_iterator const end = clip.end(); - - //StopWatch watch("GL transformed"); - const GLfixed texCoords[4][2] = { - { 0, 0 }, - { 0, 0x10000 }, - { 0x10000, 0x10000 }, - { 0x10000, 0 } + const GLfloat texCoords[4][2] = { + { 0, 0 }, + { 0, 1 }, + { 1, 1 }, + { 1, 0 } }; glMatrixMode(GL_TEXTURE); @@ -470,8 +460,8 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const } glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointer(2, GL_FIXED, 0, mVertices); - glTexCoordPointer(2, GL_FIXED, 0, texCoords); + glVertexPointer(2, GL_FLOAT, 0, mVertices); + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); while (it != end) { const Rect& r = *it++; @@ -683,6 +673,22 @@ status_t LayerBase::initializeEglImage( return err; } +void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const +{ + const Layer::State& s(drawingState()); + snprintf(buffer, SIZE, + "+ %s %p\n" + " " + "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " + "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, " + "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", + getTypeId(), this, s.z, tx(), ty(), s.w, s.h, + needsBlending(), needsDithering(), contentDirty, + s.alpha, s.flags, + s.transform[0][0], s.transform[0][1], + s.transform[1][0], s.transform[1][1]); + result.append(buffer); +} // --------------------------------------------------------------------------- @@ -715,13 +721,13 @@ LayerBaseClient::~LayerBaseClient() delete lcblk; } -int32_t LayerBaseClient::serverIndex() const +ssize_t LayerBaseClient::serverIndex() const { sp<Client> client(this->client.promote()); if (client != 0) { return (client->cid<<16)|mIndex; } - return 0xFFFF0000 | mIndex; + return ssize_t(0xFFFF0000 | mIndex); } sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() @@ -750,6 +756,21 @@ void LayerBaseClient::onRemoved() lcblk->setStatus(NO_INIT); } +void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const +{ + LayerBase::dump(result, buffer, SIZE); + + sp<Client> client(this->client.promote()); + snprintf(buffer, SIZE, + " name=%s\n" + " id=0x%08x, client=0x%08x, identity=%u\n", + getName().string(), + clientIndex(), client.get() ? client->cid : 0, + getIdentity()); + + result.append(buffer); +} + // --------------------------------------------------------------------------- LayerBaseClient::Surface::Surface( diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h index 62ec8399ad..219a53c2d7 100644 --- a/libs/surfaceflinger/LayerBase.h +++ b/libs/surfaceflinger/LayerBase.h @@ -51,35 +51,9 @@ class SurfaceFlinger; class LayerBase : public RefBase { - // poor man's dynamic_cast below - template<typename T> - struct getTypeInfoOfAnyType { - static uint32_t get() { return T::typeInfo; } - }; - - template<typename T> - struct getTypeInfoOfAnyType<T*> { - static uint32_t get() { return getTypeInfoOfAnyType<T>::get(); } - }; - public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - - template<typename T> - static T dynamicCast(LayerBase* base) { - uint32_t mostDerivedInfo = base->getTypeInfo(); - uint32_t castToInfo = getTypeInfoOfAnyType<T>::get(); - if ((mostDerivedInfo & castToInfo) == castToInfo) - return static_cast<T>(base); - return 0; - } + LayerBase(SurfaceFlinger* flinger, DisplayID display); - - LayerBase(SurfaceFlinger* flinger, DisplayID display); - DisplayID dpy; mutable bool contentDirty; Region visibleRegionScreen; @@ -125,6 +99,9 @@ public: void invalidate(); + virtual const char* getTypeId() const { return "LayerBase"; } + virtual ssize_t serverIndex() const { return -1; } + /** * draw - performs some global clipping optimizations * and calls onDraw(). @@ -217,7 +194,10 @@ public: * current list */ virtual void onRemoved() { }; - + /** always call base class first */ + virtual void dump(String8& result, char* scratch, size_t size) const; + + enum { // flags for doTransaction() eVisibleRegion = 0x00000002, }; @@ -278,7 +258,7 @@ protected: bool mTransformed; bool mUseLinearFiltering; int32_t mOrientation; - GLfixed mVertices[4][2]; + GLfloat mVertices[4][2]; Rect mTransformedBounds; int mLeft; int mTop; @@ -313,10 +293,6 @@ class LayerBaseClient : public LayerBase { public: class Surface; - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } // lcblk is (almost) only accessed from the main SF thread, in the places // where it's not, a reference to Client must be held @@ -331,14 +307,12 @@ public: inline uint32_t getIdentity() const { return mIdentity; } inline int32_t clientIndex() const { return mIndex; } - int32_t serverIndex() const; - sp<Surface> getSurface(); virtual sp<Surface> createSurface() const; - - virtual void onRemoved(); - + virtual ssize_t serverIndex() const; + virtual void onRemoved(); + virtual const char* getTypeId() const { return "LayerBaseClient"; } class Surface : public BnSurface { @@ -373,8 +347,11 @@ public: friend class Surface; +protected: + virtual void dump(String8& result, char* scratch, size_t size) const; + private: - int32_t mIndex; + int32_t mIndex; mutable Mutex mLock; mutable wp<Surface> mClientSurface; // only read diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp index 5fd7904be6..2d778763ee 100644 --- a/libs/surfaceflinger/LayerBlur.cpp +++ b/libs/surfaceflinger/LayerBlur.cpp @@ -33,11 +33,6 @@ namespace android { // --------------------------------------------------------------------------- -const uint32_t LayerBlur::typeInfo = LayerBaseClient::typeInfo | 8; -const char* const LayerBlur::typeID = "LayerBlur"; - -// --------------------------------------------------------------------------- - LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i) : LayerBaseClient(flinger, display, client, i), mCacheDirty(true), @@ -206,8 +201,8 @@ void LayerBlur::onDraw(const Region& clip) const const State& s = drawingState(); if (UNLIKELY(s.alpha < 0xFF)) { - const GGLfixed alpha = (s.alpha << 16)/255; - glColor4x(0, 0, 0, alpha); + const GLfloat alpha = s.alpha * (1.0f/255.0f); + glColor4f(0, 0, 0, alpha); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); @@ -225,38 +220,20 @@ void LayerBlur::onDraw(const Region& clip) const glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - if (UNLIKELY(transformed() - || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) { - // This is a very rare scenario. - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glScalef(mWidthScale, mHeightScale, 1); - glTranslatef(-x, mYOffset - y, 0); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointer(2, GL_FIXED, 0, mVertices); - glTexCoordPointer(2, GL_FIXED, 0, mVertices); - while (it != end) { - const Rect& r = *it++; - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - } - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } else { - // NOTE: this is marginally faster with the software gl, because - // glReadPixels() reads the fb bottom-to-top, however we'll - // skip all the jaccobian computations. - Rect r; - GLint crop[4] = { 0, 0, w, h }; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); - y = fbHeight - (y + h); - while (it != end) { - const Rect& r = *it++; - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawTexiOES(x, y, 0, w, h); - } + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(mWidthScale, mHeightScale, 1); + glTranslatef(-x, mYOffset - y, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, mVertices); + glTexCoordPointer(2, GL_FLOAT, 0, mVertices); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); } } diff --git a/libs/surfaceflinger/LayerBlur.h b/libs/surfaceflinger/LayerBlur.h index 5b63dec290..380f587158 100644 --- a/libs/surfaceflinger/LayerBlur.h +++ b/libs/surfaceflinger/LayerBlur.h @@ -31,11 +31,6 @@ namespace android { class LayerBlur : public LayerBaseClient { public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - LayerBlur(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i); virtual ~LayerBlur(); @@ -43,6 +38,7 @@ public: virtual void onDraw(const Region& clip) const; virtual bool needsBlending() const { return true; } virtual bool isSecure() const { return false; } + virtual const char* getTypeId() const { return "LayerBlur"; } virtual uint32_t doTransaction(uint32_t flags); virtual void setVisibleRegion(const Region& visibleRegion); diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp index 5c21593db2..0041a0fdc0 100644 --- a/libs/surfaceflinger/LayerBuffer.cpp +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -39,8 +39,6 @@ namespace android { // --------------------------------------------------------------------------- -const uint32_t LayerBuffer::typeInfo = LayerBaseClient::typeInfo | 0x20; -const char* const LayerBuffer::typeID = "LayerBuffer"; gralloc_module_t const* LayerBuffer::sGrallocModule = 0; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h index b1766238cd..243cc43865 100644 --- a/libs/surfaceflinger/LayerBuffer.h +++ b/libs/surfaceflinger/LayerBuffer.h @@ -52,17 +52,13 @@ class LayerBuffer : public LayerBaseClient }; public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - LayerBuffer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i); virtual ~LayerBuffer(); virtual void onFirstRef(); virtual bool needsBlending() const; + virtual const char* getTypeId() const { return "LayerBuffer"; } virtual sp<LayerBaseClient::Surface> createSurface() const; virtual status_t ditch(); diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp index fd61e30d8f..568fedbf03 100644 --- a/libs/surfaceflinger/LayerDim.cpp +++ b/libs/surfaceflinger/LayerDim.cpp @@ -30,9 +30,6 @@ namespace android { // --------------------------------------------------------------------------- -const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10; -const char* const LayerDim::typeID = "LayerDim"; - bool LayerDim::sUseTexture; GLuint LayerDim::sTexId; EGLImageKHR LayerDim::sImage; diff --git a/libs/surfaceflinger/LayerDim.h b/libs/surfaceflinger/LayerDim.h index d4672a1c44..19a9990a09 100644 --- a/libs/surfaceflinger/LayerDim.h +++ b/libs/surfaceflinger/LayerDim.h @@ -37,11 +37,6 @@ class LayerDim : public LayerBaseClient static int32_t sWidth; static int32_t sHeight; public: - static const uint32_t typeInfo; - static const char* const typeID; - virtual char const* getTypeID() const { return typeID; } - virtual uint32_t getTypeInfo() const { return typeInfo; } - LayerDim(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i); virtual ~LayerDim(); @@ -49,6 +44,7 @@ public: virtual void onDraw(const Region& clip) const; virtual bool needsBlending() const { return true; } virtual bool isSecure() const { return false; } + virtual const char* getTypeId() const { return "LayerDim"; } static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h); }; diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 0722fda55e..62d829b853 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -206,8 +206,8 @@ void SurfaceFlinger::init() property_get("debug.sf.showbackground", value, "0"); mDebugBackground = atoi(value); - LOGI_IF(mDebugRegion, "showupdates enabled"); - LOGI_IF(mDebugBackground, "showbackground enabled"); + LOGI_IF(mDebugRegion, "showupdates enabled"); + LOGI_IF(mDebugBackground, "showbackground enabled"); } SurfaceFlinger::~SurfaceFlinger() @@ -357,7 +357,6 @@ status_t SurfaceFlinger::readyToRun() dcblk->ydpi = hw.getDpiY(); dcblk->fps = hw.getRefreshRate(); dcblk->density = hw.getDensity(); - asm volatile ("":::"memory"); // Initialize OpenGL|ES glActiveTexture(GL_TEXTURE0); @@ -1079,15 +1078,15 @@ status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer) status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer) { - if (layer == 0) - return BAD_VALUE; ssize_t i = mCurrentState.layersSortedByZ.add( layer, &LayerBase::compareCurrentStateZ); - sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); - if (lbc != 0) { - mLayerMap.add(lbc->serverIndex(), lbc); - } - return NO_ERROR; + return (i < 0) ? status_t(i) : status_t(NO_ERROR); +} + +status_t SurfaceFlinger::addClientLayer_l(const sp<LayerBaseClient>& lbc) +{ + ssize_t serverIndex = lbc->serverIndex(); + return mLayerMap.add(serverIndex, lbc); } status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase) @@ -1095,10 +1094,9 @@ status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase) ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase); if (index >= 0) { mLayersRemoved = true; - sp<LayerBaseClient> layer = - LayerBase::dynamicCast< LayerBaseClient* >(layerBase.get()); - if (layer != 0) { - mLayerMap.removeItem(layer->serverIndex()); + ssize_t serverIndex = layerBase->serverIndex(); + if (serverIndex >= 0) { + mLayerMap.removeItem(serverIndex); } return NO_ERROR; } @@ -1298,7 +1296,7 @@ sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked( format = PIXEL_FORMAT_RGBA_8888; break; case PIXEL_FORMAT_OPAQUE: - format = PIXEL_FORMAT_RGB_565; + format = PIXEL_FORMAT_RGBX_8888; break; } @@ -1307,6 +1305,7 @@ sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked( if (LIKELY(err == NO_ERROR)) { layer->initStates(w, h, flags); addLayer_l(layer); + addClientLayer_l(layer); } else { LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err)); layer.clear(); @@ -1321,6 +1320,7 @@ sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked( sp<LayerBlur> layer = new LayerBlur(this, display, client, id); layer->initStates(w, h, flags); addLayer_l(layer); + addClientLayer_l(layer); return layer; } @@ -1331,6 +1331,7 @@ sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked( sp<LayerDim> layer = new LayerDim(this, display, client, id); layer->initStates(w, h, flags); addLayer_l(layer); + addClientLayer_l(layer); return layer; } @@ -1341,6 +1342,7 @@ sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked( sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id); layer->initStates(w, h, flags); addLayer_l(layer); + addClientLayer_l(layer); return layer; } @@ -1512,83 +1514,17 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) result.append(buffer); } - size_t s = mClientsMap.size(); - char name[64]; - for (size_t i=0 ; i<s ; i++) { - sp<Client> client = mClientsMap.valueAt(i); - sprintf(name, " Client (id=0x%08x)", client->cid); - client->dump(name); - } const LayerVector& currentLayers = mCurrentState.layersSortedByZ; const size_t count = currentLayers.size(); for (size_t i=0 ; i<count ; i++) { - /*** LayerBase ***/ - const sp<LayerBase>& layer = currentLayers[i]; - const Layer::State& s = layer->drawingState(); - snprintf(buffer, SIZE, - "+ %s %p\n" - " " - "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " - "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, " - "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", - layer->getTypeID(), layer.get(), - s.z, layer->tx(), layer->ty(), s.w, s.h, - layer->needsBlending(), layer->needsDithering(), - layer->contentDirty, - s.alpha, s.flags, - s.transform[0][0], s.transform[0][1], - s.transform[1][0], s.transform[1][1]); - result.append(buffer); - buffer[0] = 0; - /*** LayerBaseClient ***/ - sp<LayerBaseClient> lbc = - LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); - if (lbc != 0) { - sp<Client> client(lbc->client.promote()); - snprintf(buffer, SIZE, - " name=%s\n", lbc->getName().string()); - result.append(buffer); - snprintf(buffer, SIZE, - " id=0x%08x, client=0x%08x, identity=%u\n", - lbc->clientIndex(), client.get() ? client->cid : 0, - lbc->getIdentity()); - - result.append(buffer); - buffer[0] = 0; - } - /*** Layer ***/ - sp<Layer> l = LayerBase::dynamicCast< Layer* >(layer.get()); - if (l != 0) { - SharedBufferStack::Statistics stats = l->lcblk->getStats(); - result.append( l->lcblk->dump(" ") ); - sp<const GraphicBuffer> buf0(l->getBuffer(0)); - sp<const GraphicBuffer> buf1(l->getBuffer(1)); - uint32_t w0=0, h0=0, s0=0; - uint32_t w1=0, h1=0, s1=0; - if (buf0 != 0) { - w0 = buf0->getWidth(); - h0 = buf0->getHeight(); - s0 = buf0->getStride(); - } - if (buf1 != 0) { - w1 = buf1->getWidth(); - h1 = buf1->getHeight(); - s1 = buf1->getStride(); - } - snprintf(buffer, SIZE, - " " - "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u]," - " freezeLock=%p, dq-q-time=%u us\n", - l->pixelFormat(), - w0, h0, s0, w1, h1, s1, - l->getFreezeLock().get(), stats.totalTime); - result.append(buffer); - buffer[0] = 0; - } + const sp<LayerBase>& layer(currentLayers[i]); + layer->dump(result, buffer, SIZE); + const Layer::State& s(layer->drawingState()); s.transparentRegion.dump(result, "transparentRegion"); layer->transparentRegionScreen.dump(result, "transparentRegionScreen"); layer->visibleRegionScreen.dump(result, "visibleRegionScreen"); } + mWormholeRegion.dump(result, "WormholeRegion"); const DisplayHardware& hw(graphicPlane(0).displayHardware()); snprintf(buffer, SIZE, @@ -1601,16 +1537,19 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) " last transaction time : %f us\n", mLastSwapBufferTime/1000.0, mLastTransactionTime/1000.0); result.append(buffer); + if (inSwapBuffersDuration || !locked) { snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n", inSwapBuffersDuration/1000.0); result.append(buffer); } + if (inTransactionDuration || !locked) { snprintf(buffer, SIZE, " transaction time: %f us\n", inTransactionDuration/1000.0); result.append(buffer); } + snprintf(buffer, SIZE, " client count: %d\n", mClientsMap.size()); result.append(buffer); const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); @@ -1773,10 +1712,6 @@ sp<LayerBaseClient> Client::getLayerUser(int32_t i) const { return lbc; } -void Client::dump(const char* what) -{ -} - // --------------------------------------------------------------------------- #if 0 #pragma mark - diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index d75dc15f18..9c8de512ed 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -73,7 +73,6 @@ public: inline bool isValid(int32_t i) const; sp<LayerBaseClient> getLayerUser(int32_t i) const; - void dump(const char* what); const Vector< wp<LayerBaseClient> >& getLayers() const { return mLayers; @@ -281,6 +280,7 @@ private: void destroyConnection(ClientID cid); sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const; status_t addLayer_l(const sp<LayerBase>& layer); + status_t addClientLayer_l(const sp<LayerBaseClient>& lbc); status_t removeLayer_l(const sp<LayerBase>& layer); status_t purgatorizeLayer_l(const sp<LayerBase>& layer); void free_resources_l(); diff --git a/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp index 175f989ef0..5e27cc9bd9 100644 --- a/libs/surfaceflinger/Transform.cpp +++ b/libs/surfaceflinger/Transform.cpp @@ -229,14 +229,13 @@ Transform::vec3 Transform::transform(const vec3& v) const { return r; } -void Transform::transform(fixed1616* point, int x, int y) const +void Transform::transform(float* point, int x, int y) const { - const float toFixed = 65536.0f; const mat33& M(mMatrix); vec2 v(x, y); v = transform(v); - point[0] = v[0] * toFixed; - point[1] = v[1] * toFixed; + point[0] = v[0]; + point[1] = v[1]; } Rect Transform::makeBounds(int w, int h) const diff --git a/libs/surfaceflinger/Transform.h b/libs/surfaceflinger/Transform.h index 2e5b893ece..20fa11a46a 100644 --- a/libs/surfaceflinger/Transform.h +++ b/libs/surfaceflinger/Transform.h @@ -37,8 +37,6 @@ public: explicit Transform(uint32_t orientation); ~Transform(); - typedef int32_t fixed1616; - // FIXME: must match OVERLAY_TRANSFORM_*, pull from hardware.h enum orientation_flags { ROT_0 = 0x00000000, @@ -76,7 +74,7 @@ public: // transform data Rect makeBounds(int w, int h) const; - void transform(fixed1616* point, int x, int y) const; + void transform(float* point, int x, int y) const; Region transform(const Region& reg) const; Transform operator * (const Transform& rhs) const; diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp index a17e8acc57..c42cd5315b 100644 --- a/libs/surfaceflinger_client/SharedBufferStack.cpp +++ b/libs/surfaceflinger_client/SharedBufferStack.cpp @@ -67,19 +67,47 @@ void SharedBufferStack::init(int32_t i) identity = i; } +status_t SharedBufferStack::setCrop(int buffer, const Rect& crop) +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return BAD_INDEX; + + buffers[buffer].crop.l = uint16_t(crop.left); + buffers[buffer].crop.t = uint16_t(crop.top); + buffers[buffer].crop.r = uint16_t(crop.right); + buffers[buffer].crop.b = uint16_t(crop.bottom); + return NO_ERROR; +} + status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty) { if (uint32_t(buffer) >= NUM_BUFFER_MAX) return BAD_INDEX; - // in the current implementation we only send a single rectangle - const Rect bounds(dirty.getBounds()); - FlatRegion& reg(dirtyRegion[buffer]); - reg.count = 1; - reg.rects[0] = uint16_t(bounds.left); - reg.rects[1] = uint16_t(bounds.top); - reg.rects[2] = uint16_t(bounds.right); - reg.rects[3] = uint16_t(bounds.bottom); + FlatRegion& reg(buffers[buffer].dirtyRegion); + if (dirty.isEmpty()) { + reg.count = 0; + return NO_ERROR; + } + + size_t count; + Rect const* r = dirty.getArray(&count); + if (count > FlatRegion::NUM_RECT_MAX) { + const Rect bounds(dirty.getBounds()); + reg.count = 1; + reg.rects[0].l = uint16_t(bounds.left); + reg.rects[0].t = uint16_t(bounds.top); + reg.rects[0].r = uint16_t(bounds.right); + reg.rects[0].b = uint16_t(bounds.bottom); + } else { + reg.count = count; + for (size_t i=0 ; i<count ; i++) { + reg.rects[i].l = uint16_t(r[i].left); + reg.rects[i].t = uint16_t(r[i].top); + reg.rects[i].r = uint16_t(r[i].right); + reg.rects[i].b = uint16_t(r[i].bottom); + } + } return NO_ERROR; } @@ -89,8 +117,27 @@ Region SharedBufferStack::getDirtyRegion(int buffer) const if (uint32_t(buffer) >= NUM_BUFFER_MAX) return res; - const FlatRegion& reg(dirtyRegion[buffer]); - res.set(Rect(reg.rects[0], reg.rects[1], reg.rects[2], reg.rects[3])); + const FlatRegion& reg(buffers[buffer].dirtyRegion); + if (reg.count > FlatRegion::NUM_RECT_MAX) + return res; + + if (reg.count == 1) { + const Rect r( + reg.rects[0].l, + reg.rects[0].t, + reg.rects[0].r, + reg.rects[0].b); + res.set(r); + } else { + for (size_t i=0 ; i<reg.count ; i++) { + const Rect r( + reg.rects[i].l, + reg.rects[i].t, + reg.rects[i].r, + reg.rects[i].b); + res.orSelf(r); + } + } return res; } @@ -132,7 +179,7 @@ String8 SharedBufferBase::dump(char const* prefix) const char buffer[SIZE]; String8 result; SharedBufferStack& stack( *mSharedStack ); - int tail = (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers; + int tail = computeTail(); snprintf(buffer, SIZE, "%s[ head=%2d, available=%2d, queued=%2d, tail=%2d ] " "reallocMask=%08x, inUse=%2d, identity=%d, status=%d\n", @@ -142,6 +189,48 @@ String8 SharedBufferBase::dump(char const* prefix) const return result; } +int32_t SharedBufferBase::computeTail() const +{ + SharedBufferStack& stack( *mSharedStack ); + return (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers; +} + +status_t SharedBufferBase::waitForCondition(const ConditionBase& condition) +{ + const SharedBufferStack& stack( *mSharedStack ); + SharedClient& client( *mSharedClient ); + const nsecs_t TIMEOUT = s2ns(1); + const int identity = mIdentity; + + Mutex::Autolock _l(client.lock); + while ((condition()==false) && + (stack.identity == identity) && + (stack.status == NO_ERROR)) + { + status_t err = client.cv.waitRelative(client.lock, TIMEOUT); + // handle errors and timeouts + if (CC_UNLIKELY(err != NO_ERROR)) { + if (err == TIMED_OUT) { + if (condition()) { + LOGE("waitForCondition(%s) timed out (identity=%d), " + "but condition is true! We recovered but it " + "shouldn't happen." , condition.name(), stack.identity); + break; + } else { + LOGW("waitForCondition(%s) timed out " + "(identity=%d, status=%d). " + "CPU may be pegged. trying again.", condition.name(), + stack.identity, stack.status); + } + } else { + LOGE("waitForCondition(%s) error (%s) ", + condition.name(), strerror(-err)); + return err; + } + } + } + return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status; +} // ============================================================================ // conditions and updates // ============================================================================ @@ -149,24 +238,34 @@ String8 SharedBufferBase::dump(char const* prefix) const SharedBufferClient::DequeueCondition::DequeueCondition( SharedBufferClient* sbc) : ConditionBase(sbc) { } -bool SharedBufferClient::DequeueCondition::operator()() { +bool SharedBufferClient::DequeueCondition::operator()() const { return stack.available > 0; } SharedBufferClient::LockCondition::LockCondition( SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) { } -bool SharedBufferClient::LockCondition::operator()() { - return (buf != stack.head || +bool SharedBufferClient::LockCondition::operator()() const { + // NOTE: if stack.head is messed up, we could crash the client + // or cause some drawing artifacts. This is okay, as long as it is + // limited to the client. + return (buf != stack.index[stack.head] || (stack.queued > 0 && stack.inUse != buf)); } SharedBufferServer::ReallocateCondition::ReallocateCondition( SharedBufferBase* sbb, int buf) : ConditionBase(sbb), buf(buf) { } -bool SharedBufferServer::ReallocateCondition::operator()() { +bool SharedBufferServer::ReallocateCondition::operator()() const { + int32_t head = stack.head; + if (uint32_t(head) >= NUM_BUFFER_MAX) { + // if stack.head is messed up, we cannot allow the server to + // crash (since stack.head is mapped on the client side) + stack.status = BAD_VALUE; + return false; + } // TODO: we should also check that buf has been dequeued - return (buf != stack.head); + return (buf != stack.index[head]); } // ---------------------------------------------------------------------------- @@ -208,9 +307,11 @@ SharedBufferServer::RetireUpdate::RetireUpdate( ssize_t SharedBufferServer::RetireUpdate::operator()() { // head is only written in this function, which is single-thread. int32_t head = stack.head; + if (uint32_t(head) >= NUM_BUFFER_MAX) + return BAD_VALUE; // Preventively lock the current buffer before updating queued. - android_atomic_write(head, &stack.inUse); + android_atomic_write(stack.index[head], &stack.inUse); // Decrement the number of queued buffers int32_t queued; @@ -226,7 +327,7 @@ ssize_t SharedBufferServer::RetireUpdate::operator()() { // lock the buffer before advancing head, which automatically unlocks // the buffer we preventively locked upon entering this function - android_atomic_write(head, &stack.inUse); + android_atomic_write(stack.index[head], &stack.inUse); // advance head android_atomic_write(head, &stack.head); @@ -250,37 +351,19 @@ ssize_t SharedBufferServer::StatusUpdate::operator()() { SharedBufferClient::SharedBufferClient(SharedClient* sharedClient, int surface, int num, int32_t identity) - : SharedBufferBase(sharedClient, surface, num, identity), tail(0) -{ - tail = computeTail(); -} - -int32_t SharedBufferClient::computeTail() const + : SharedBufferBase(sharedClient, surface, num, identity), + tail(0), undoDequeueTail(0) { SharedBufferStack& stack( *mSharedStack ); - // we need to make sure we read available and head coherently, - // w.r.t RetireUpdate. - int32_t newTail; - int32_t avail; - int32_t head; - do { - avail = stack.available; - head = stack.head; - } while (stack.available != avail); - newTail = head - avail + 1; - if (newTail < 0) { - newTail += mNumBuffers; - } else if (newTail >= mNumBuffers) { - newTail -= mNumBuffers; - } - return newTail; + tail = computeTail(); + queued_head = stack.head; } ssize_t SharedBufferClient::dequeue() { SharedBufferStack& stack( *mSharedStack ); - if (stack.head == tail && stack.available == 2) { + if (stack.head == tail && stack.available == mNumBuffers) { LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d", tail, stack.head, stack.available, stack.queued); } @@ -301,9 +384,10 @@ ssize_t SharedBufferClient::dequeue() LOGW("dequeue probably called from multiple threads!"); } - int dequeued = tail; + undoDequeueTail = tail; + int dequeued = stack.index[tail]; tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1); - LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail=%d, %s", + LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s", dequeued, tail, dump("").string()); mDequeueTime[dequeued] = dequeueTime; @@ -313,16 +397,19 @@ ssize_t SharedBufferClient::dequeue() status_t SharedBufferClient::undoDequeue(int buf) { + // TODO: we can only undo the previous dequeue, we should + // enforce that in the api UndoDequeueUpdate update(this); status_t err = updateCondition( update ); if (err == NO_ERROR) { - tail = computeTail(); + tail = undoDequeueTail; } return err; } status_t SharedBufferClient::lock(int buf) { + SharedBufferStack& stack( *mSharedStack ); LockCondition condition(this, buf); status_t err = waitForCondition(condition); return err; @@ -330,26 +417,37 @@ status_t SharedBufferClient::lock(int buf) status_t SharedBufferClient::queue(int buf) { + SharedBufferStack& stack( *mSharedStack ); + + queued_head = ((queued_head+1 >= mNumBuffers) ? 0 : queued_head+1); + stack.index[queued_head] = buf; + QueueUpdate update(this); status_t err = updateCondition( update ); LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string()); - SharedBufferStack& stack( *mSharedStack ); + const nsecs_t now = systemTime(SYSTEM_TIME_THREAD); stack.stats.totalTime = ns2us(now - mDequeueTime[buf]); return err; } -bool SharedBufferClient::needNewBuffer(int buffer) const +bool SharedBufferClient::needNewBuffer(int buf) const { SharedBufferStack& stack( *mSharedStack ); - const uint32_t mask = 1<<buffer; + const uint32_t mask = 1<<buf; return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0; } -status_t SharedBufferClient::setDirtyRegion(int buffer, const Region& reg) +status_t SharedBufferClient::setCrop(int buf, const Rect& crop) { SharedBufferStack& stack( *mSharedStack ); - return stack.setDirtyRegion(buffer, reg); + return stack.setCrop(buf, crop); +} + +status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg) +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.setDirtyRegion(buf, reg); } // ---------------------------------------------------------------------------- @@ -363,20 +461,30 @@ SharedBufferServer::SharedBufferServer(SharedClient* sharedClient, mSharedStack->available = num; mSharedStack->queued = 0; mSharedStack->reallocMask = 0; - memset(mSharedStack->dirtyRegion, 0, sizeof(mSharedStack->dirtyRegion)); + memset(mSharedStack->buffers, 0, sizeof(mSharedStack->buffers)); + for (int i=0 ; i<num ; i++) { + mSharedStack->index[i] = i; + } } ssize_t SharedBufferServer::retireAndLock() { RetireUpdate update(this, mNumBuffers); ssize_t buf = updateCondition( update ); - LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", int(buf), dump("").string()); + if (buf >= 0) { + if (uint32_t(buf) >= NUM_BUFFER_MAX) + return BAD_VALUE; + SharedBufferStack& stack( *mSharedStack ); + buf = stack.index[buf]; + LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", + int(buf), dump("").string()); + } return buf; } -status_t SharedBufferServer::unlock(int buffer) +status_t SharedBufferServer::unlock(int buf) { - UnlockUpdate update(this, buffer); + UnlockUpdate update(this, buf); status_t err = updateCondition( update ); return err; } @@ -403,17 +511,17 @@ int32_t SharedBufferServer::getQueuedCount() const return stack.queued; } -status_t SharedBufferServer::assertReallocate(int buffer) +status_t SharedBufferServer::assertReallocate(int buf) { - ReallocateCondition condition(this, buffer); + ReallocateCondition condition(this, buf); status_t err = waitForCondition(condition); return err; } -Region SharedBufferServer::getDirtyRegion(int buffer) const +Region SharedBufferServer::getDirtyRegion(int buf) const { SharedBufferStack& stack( *mSharedStack ); - return stack.getDirtyRegion(buffer); + return stack.getDirtyRegion(buf); } SharedBufferStack::Statistics SharedBufferServer::getStats() const diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp index 5dd75c3213..eee4dae3d6 100644 --- a/libs/surfaceflinger_client/Surface.cpp +++ b/libs/surfaceflinger_client/Surface.cpp @@ -17,8 +17,6 @@ #define LOG_TAG "Surface" #include <stdint.h> -#include <unistd.h> -#include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> @@ -28,8 +26,6 @@ #include <utils/CallStack.h> #include <utils/Log.h> -#include <pixelflinger/pixelflinger.h> - #include <binder/IPCThreadState.h> #include <binder/IMemory.h> @@ -55,6 +51,8 @@ static status_t copyBlt( const sp<GraphicBuffer>& src, const Region& reg) { + // src and dst with, height and format must be identical. no verification + // is done here. status_t err; uint8_t const * src_bits = NULL; err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits); @@ -67,7 +65,6 @@ static status_t copyBlt( Region::const_iterator head(reg.begin()); Region::const_iterator tail(reg.end()); if (head != tail && src_bits && dst_bits) { - // NOTE: dst and src must be the same format const size_t bpp = bytesPerPixel(src->format); const size_t dbpr = dst->stride * bpp; const size_t sbpr = src->stride * bpp; @@ -354,7 +351,6 @@ void Surface::init() // be default we request a hardware surface mUsage = GRALLOC_USAGE_HW_RENDER; mConnected = 0; - mNeedFullUpdate = false; } Surface::~Surface() @@ -467,18 +463,6 @@ int Surface::perform(android_native_window_t* window, // ---------------------------------------------------------------------------- -status_t Surface::dequeueBuffer(sp<GraphicBuffer>* buffer) { - android_native_buffer_t* out; - status_t err = dequeueBuffer(&out); - if (err == NO_ERROR) { - *buffer = GraphicBuffer::getSelf(out); - } - return err; -} - -// ---------------------------------------------------------------------------- - - int Surface::dequeueBuffer(android_native_buffer_t** buffer) { sp<SurfaceComposerClient> client(getClient()); @@ -495,9 +479,12 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer) // below we make sure we AT LEAST have the usage flags we want const uint32_t usage(getUsage()); const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); + + // Always call needNewBuffer(), since it clears the needed buffers flags + bool needNewBuffer = mSharedBufferClient->needNewBuffer(bufIdx); if (backBuffer == 0 || ((uint32_t(backBuffer->usage) & usage) != usage) || - mSharedBufferClient->needNewBuffer(bufIdx)) + needNewBuffer) { err = getBufferLocked(bufIdx, usage); LOGE_IF(err, "getBufferLocked(%ld, %08x) failed (%s)", @@ -531,7 +518,7 @@ int Surface::lockBuffer(android_native_buffer_t* buffer) if (err != NO_ERROR) return err; - int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex(); + int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); err = mSharedBufferClient->lock(bufIdx); LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err)); return err; @@ -548,7 +535,8 @@ int Surface::queueBuffer(android_native_buffer_t* buffer) mDirtyRegion.set(mSwapRectangle); } - int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex(); + int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); + mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop); mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion); err = mSharedBufferClient->queue(bufIdx); LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err)); @@ -578,6 +566,10 @@ int Surface::query(int what, int* value) int Surface::perform(int operation, va_list args) { + status_t err = validate(); + if (err != NO_ERROR) + return err; + int res = NO_ERROR; switch (operation) { case NATIVE_WINDOW_SET_USAGE: @@ -589,6 +581,9 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_DISCONNECT: res = dispatch_disconnect( args ); break; + case NATIVE_WINDOW_SET_CROP: + res = dispatch_crop( args ); + break; default: res = NAME_NOT_FOUND; break; @@ -608,6 +603,10 @@ int Surface::dispatch_disconnect(va_list args) { int api = va_arg(args, int); return disconnect( api ); } +int Surface::dispatch_crop(va_list args) { + android_native_rect_t const* rect = va_arg(args, android_native_rect_t*); + return crop( reinterpret_cast<Rect const*>(rect) ); +} void Surface::setUsage(uint32_t reqUsage) @@ -666,6 +665,14 @@ int Surface::getConnectedApi() const return mConnected; } +int Surface::crop(Rect const* rect) +{ + Mutex::Autolock _l(mSurfaceLock); + // TODO: validate rect size + mNextBufferCrop = *rect; + return NO_ERROR; +} + // ---------------------------------------------------------------------------- @@ -703,45 +710,47 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) // we're intending to do software rendering from this point setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); - sp<GraphicBuffer> backBuffer; - status_t err = dequeueBuffer(&backBuffer); + android_native_buffer_t* out; + status_t err = dequeueBuffer(&out); LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err)); if (err == NO_ERROR) { + sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); err = lockBuffer(backBuffer.get()); LOGE_IF(err, "lockBuffer (idx=%d) failed (%s)", - backBuffer->getIndex(), strerror(-err)); + getBufferIndex(backBuffer), strerror(-err)); if (err == NO_ERROR) { - // we handle copy-back here... - const Rect bounds(backBuffer->width, backBuffer->height); - Region scratch(bounds); + const Region boundsRegion(bounds); + Region scratch(boundsRegion); Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch); + newDirtyRegion &= boundsRegion; - if (mNeedFullUpdate) { - // reset newDirtyRegion to bounds when a buffer is reallocated - // it would be better if this information was associated with - // the buffer and made available to outside of Surface. - // This will do for now though. - mNeedFullUpdate = false; - newDirtyRegion.set(bounds); - } else { - newDirtyRegion.andSelf(bounds); - } - + // figure out if we can copy the frontbuffer back const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); - if (frontBuffer !=0 && - backBuffer->width == frontBuffer->width && - backBuffer->height == frontBuffer->height && - !(mFlags & ISurfaceComposer::eDestroyBackbuffer)) - { + const bool canCopyBack = (frontBuffer != 0 && + backBuffer->width == frontBuffer->width && + backBuffer->height == frontBuffer->height && + backBuffer->format == frontBuffer->format && + !(mFlags & ISurfaceComposer::eDestroyBackbuffer)); + + // the dirty region we report to surfaceflinger is the one + // given by the user (as opposed to the one *we* return to the + // user). + mDirtyRegion = newDirtyRegion; + + if (canCopyBack) { + // copy the area that is invalid and not repainted this round const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion)); - if (!copyback.isEmpty() && frontBuffer!=0) { - // copy front to back + if (!copyback.isEmpty()) copyBlt(backBuffer, frontBuffer, copyback); - } + } else { + // if we can't copy-back anything, modify the user's dirty + // region to make sure they redraw the whole buffer + newDirtyRegion = boundsRegion; } - mDirtyRegion = newDirtyRegion; + // keep track of the are of the buffer that is "clean" + // (ie: that will be redrawn) mOldDirtyRegion = newDirtyRegion; void* vaddr; @@ -777,7 +786,7 @@ status_t Surface::unlockAndPost() err = queueBuffer(mLockedBuffer.get()); LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)", - mLockedBuffer->getIndex(), strerror(-err)); + getBufferIndex(mLockedBuffer), strerror(-err)); mPostedBuffer = mLockedBuffer; mLockedBuffer = 0; @@ -789,6 +798,11 @@ void Surface::setSwapRectangle(const Rect& r) { mSwapRectangle = r; } +int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const +{ + return buffer->getIndex(); +} + status_t Surface::getBufferLocked(int index, int usage) { sp<ISurface> s(mSurface); @@ -820,7 +834,6 @@ status_t Surface::getBufferLocked(int index, int usage) if (err == NO_ERROR) { currentBuffer = buffer; currentBuffer->setIndex(index); - mNeedFullUpdate = true; } } else { err = err<0 ? err : NO_MEMORY; diff --git a/libs/surfaceflinger_client/tests/Android.mk b/libs/surfaceflinger_client/tests/Android.mk new file mode 100644 index 0000000000..5053e7d643 --- /dev/null +++ b/libs/surfaceflinger_client/tests/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk b/libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk new file mode 100644 index 0000000000..d3dfe04285 --- /dev/null +++ b/libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + SharedBufferStackTest.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui \ + libsurfaceflinger_client + +LOCAL_MODULE:= test-sharedbufferstack + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp new file mode 100644 index 0000000000..67325806dd --- /dev/null +++ b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2007 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. + */ + +#undef NDEBUG + +#include <assert.h> +#include <cutils/memory.h> +#include <cutils/log.h> +#include <utils/Errors.h> +#include <private/surfaceflinger/SharedBufferStack.h> + +using namespace android; + + +void log(const char* prefix, int *b, size_t num) +{ + printf("%s: ", prefix); + for (size_t i=0 ; i<num ; i++) { + printf("%d ", b[i]); + } + printf("\n"); +} + +int main(int argc, char** argv) +{ + status_t err; + const size_t num = 4; + SharedClient client; + SharedBufferServer s(&client, 0, num, 0); + SharedBufferClient c(&client, 0, num, 0); + int b[num], u[num], r[num]; + + for (size_t i=0 ; i<num ; i++) { + b[i] = c.dequeue(); + assert(b[i]==i); + } + log("DQ", b, num); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.lock(b[i]); + assert(err==0); + } + log("LK", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.queue(b[i]); + assert(err==0); + } + log(" Q", b, num-1); + + + for (size_t i=0 ; i<num-1 ; i++) { + r[i] = s.retireAndLock(); + assert(r[i]==i); + err = s.unlock(r[i]); + assert(err == 0); + } + log("RT", r, num-1); + + err = c.lock(b[num-1]); + assert(err == 0); + log("LK", b+num-1, 1); + + err = c.queue(b[num-1]); + assert(err == 0); + log(" Q", b+num-1, 1); + + r[num-1] = s.retireAndLock(); + assert(r[num-1]==num-1); + err = s.unlock(r[num-1]); + assert(err == 0); + log("RT", r+num-1, 1); + + // ------------------------------------ + printf("\n"); + + for (size_t i=0 ; i<num ; i++) { + b[i] = c.dequeue(); + assert(b[i]==i); + } + log("DQ", b, num); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.lock(b[i]); + assert(err==0); + } + log("LK", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + u[i] = b[num-2-i]; + } + u[num-1] = num-1; + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.queue(u[i]); + assert(err==0); + } + log(" Q", u, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + r[i] = s.retireAndLock(); + assert(r[i]==u[i]); + err = s.unlock(r[i]); + assert(err == 0); + } + log("RT", r, num-1); + + err = c.lock(b[num-1]); + assert(err == 0); + log("LK", b+num-1, 1); + + err = c.queue(b[num-1]); + assert(err == 0); + log(" Q", b+num-1, 1); + + r[num-1] = s.retireAndLock(); + assert(r[num-1]==num-1); + err = s.unlock(r[num-1]); + assert(err == 0); + log("RT", r+num-1, 1); + + // ------------------------------------ + printf("\n"); + + for (size_t i=0 ; i<num ; i++) { + b[i] = c.dequeue(); + assert(b[i]==u[i]); + } + log("DQ", b, num); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.lock(b[i]); + assert(err==0); + } + log("LK", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.queue(b[i]); + assert(err==0); + } + log(" Q", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + r[i] = s.retireAndLock(); + assert(r[i]==u[i]); + err = s.unlock(r[i]); + assert(err == 0); + } + log("RT", r, num-1); + + err = c.lock(u[num-1]); + assert(err == 0); + log("LK", u+num-1, 1); + + err = c.queue(u[num-1]); + assert(err == 0); + log(" Q", u+num-1, 1); + + r[num-1] = s.retireAndLock(); + assert(r[num-1]==num-1); + err = s.unlock(r[num-1]); + assert(err == 0); + log("RT", r+num-1, 1); + + // ------------------------------------ + printf("\n"); + + b[0] = c.dequeue(); + assert(b[0]==u[0]); + log("DQ", b, 1); + + c.undoDequeue(b[0]); + assert(err == 0); + log("UDQ", b, 1); + + // ------------------------------------ + printf("\n"); + + for (size_t i=0 ; i<num ; i++) { + b[i] = c.dequeue(); + assert(b[i]==u[i]); + } + log("DQ", b, num); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.lock(b[i]); + assert(err==0); + } + log("LK", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + err = c.queue(b[i]); + assert(err==0); + } + log(" Q", b, num-1); + + for (size_t i=0 ; i<num-1 ; i++) { + r[i] = s.retireAndLock(); + assert(r[i]==u[i]); + err = s.unlock(r[i]); + assert(err == 0); + } + log("RT", r, num-1); + + err = c.lock(u[num-1]); + assert(err == 0); + log("LK", u+num-1, 1); + + err = c.queue(u[num-1]); + assert(err == 0); + log(" Q", u+num-1, 1); + + r[num-1] = s.retireAndLock(); + assert(r[num-1]==num-1); + err = s.unlock(r[num-1]); + assert(err == 0); + log("RT", r+num-1, 1); + + return 0; +} diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 6ae7e741b6..d51664dded 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -15,6 +15,8 @@ ** limitations under the License. */ +#define LOG_TAG "GraphicBufferAllocator" + #include <cutils/log.h> #include <utils/Singleton.h> @@ -61,9 +63,9 @@ void GraphicBufferAllocator::dump(String8& result) const const size_t c = list.size(); for (size_t i=0 ; i<c ; i++) { const alloc_rec_t& rec(list.valueAt(i)); - snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u x %4u | %2d | 0x%08x\n", + snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %2d | 0x%08x\n", list.keyAt(i), rec.size/1024.0f, - rec.w, rec.h, rec.format, rec.usage); + rec.w, rec.s, rec.h, rec.format, rec.usage); result.append(buffer); total += rec.size; } @@ -71,16 +73,13 @@ void GraphicBufferAllocator::dump(String8& result) const result.append(buffer); } -static inline uint32_t clamp(uint32_t c) { - return c>0 ? c : 1; -} - status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format, int usage, buffer_handle_t* handle, int32_t* stride) { - // make sure to not allocate a 0 x 0 buffer - w = clamp(w); - h = clamp(h); + // make sure to not allocate a N x 0 or 0 x N buffer, since this is + // allowed from an API stand-point allocate a 1x1 buffer instead. + if (!w || !h) + w = h = 1; // we have a h/w allocator and h/w buffer is requested status_t err; @@ -100,9 +99,9 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma alloc_rec_t rec; rec.w = w; rec.h = h; + rec.s = *stride; rec.format = format; rec.usage = usage; - rec.vaddr = 0; rec.size = h * stride[0] * bytesPerPixel(format); list.add(*handle, rec); } else { diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 7e0f881af6..a1401addf3 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4178,6 +4178,9 @@ void ResTable::print(bool inclValues) const case ResTable_config::SCREENSIZE_LARGE: printf(" (large)"); break; + case ResTable_config::SCREENSIZE_XLARGE: + printf(" (xlarge)"); + break; } printf(" lng=%d", type->config.screenLayout&ResTable_config::MASK_SCREENLONG); diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 89b3e1f2ab..e6cf792443 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -239,7 +239,7 @@ struct tls_t // ---------------------------------------------------------------------------- -egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS]; +static egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS]; static egl_display_t gDisplay[NUM_DISPLAYS]; static pthread_mutex_t gThreadLocalStorageKeyMutex = PTHREAD_MUTEX_INITIALIZER; static pthread_key_t gEGLThreadLocalStorageKey = -1; diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h index 1fba209f77..c8f529ac05 100644 --- a/opengl/libs/egl_impl.h +++ b/opengl/libs/egl_impl.h @@ -31,6 +31,7 @@ namespace android { struct egl_connection_t { + inline egl_connection_t() : dso(0) { } void * dso; gl_hooks_t * hooks[2]; EGLint major; diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java index ce522c848f..ce40b5d0c1 100644 --- a/vpn/java/android/net/vpn/VpnManager.java +++ b/vpn/java/android/net/vpn/VpnManager.java @@ -85,7 +85,8 @@ public class VpnManager { // TODO(oam): Test VPN when EFS is enabled (will do later)... public static String getProfilePath() { - return Environment.getDataDirectory().getPath() + PROFILES_PATH; + // This call will return the correct path if Encrypted FS is enabled or not. + return Environment.getSecureDataDirectory().getPath() + PROFILES_PATH; } /** |