diff options
author | Kenny Root <kroot@google.com> | 2010-06-01 19:56:42 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-06-01 19:56:42 -0700 |
commit | 2846041031d84869c3ee0af5e834b975697aa996 (patch) | |
tree | ce2620bfcd981e0b9a023ba424f4dfb00c53ec4a | |
parent | 7c7eadeb4f72878bbbb76e3353767985ed024033 (diff) | |
parent | caf742fdf5122799233f851569535ea3c51396a7 (diff) | |
download | frameworks_native-2846041031d84869c3ee0af5e834b975697aa996.zip frameworks_native-2846041031d84869c3ee0af5e834b975697aa996.tar.gz frameworks_native-2846041031d84869c3ee0af5e834b975697aa996.tar.bz2 |
am 89bc1395: Merge "Add more error checking for ndc" into froyo
Merge commit '89bc1395748a0a139db82adbf01166ed6fa79766' into froyo-plus-aosp
* commit '89bc1395748a0a139db82adbf01166ed6fa79766': (392 commits)
Add more error checking for ndc
Add javadoc for camera scene modes.
more clean-up in preparation of bigger changes
Fix a typo in Singleton<>
Avoid copying for input recording frames in CameraSource
Add video frame parameter.
more clean-up of Surfaceflinger's client management
Better sniffing of MPEG4 content by checking for a compatible sub-brand.
Remove the logic in CameraSource using CameraParameters class to find out the buffer size.
Add support for gps altitude EXIF tags.
minor clean-up GLclampx -> GLclampf
clean-up dead-code
Make sure to use filtering while in fixed-size mode
oopsie. forgot to displatch SET_BUFFERS_GEOMETRY
Avoid repeatedly allocating and freeing memory in CameraSource
After failing to create an AudioTrack, only stop the source if we were the ones starting it.
Convert a number of assertions into runtime errors (OggExtractor)
fix [2712278] The preview buffer left some black borders in left and bottom edges
Fix 2667859: change text back to default size
Support for thumbnail extraction in the Matroska extractor.
...
69 files changed, 4397 insertions, 3214 deletions
diff --git a/camera/libcameraservice/Android.mk b/camera/libcameraservice/Android.mk index df5c166..a0d6ee1 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 8b66389..fda48e8 100644 --- a/camera/libcameraservice/CameraHardwareStub.cpp +++ b/camera/libcameraservice/CameraHardwareStub.cpp @@ -47,14 +47,14 @@ void CameraHardwareStub::initDefaultParameters() { CameraParameters p; - p.set("preview-size-values","320x240"); + p.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240"); p.setPreviewSize(320, 240); p.setPreviewFrameRate(15); - p.setPreviewFormat("yuv422sp"); + p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP); - p.set("picture-size-values", "320x240"); + p.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "320x240"); p.setPictureSize(320, 240); - p.setPictureFormat("jpeg"); + p.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG); if (setParameters(p) != NO_ERROR) { LOGE("Failed to set default parameters?!"); @@ -66,14 +66,14 @@ void CameraHardwareStub::initHeapLocked() // Create raw heap. int picture_width, picture_height; mParameters.getPictureSize(&picture_width, &picture_height); - mRawHeap = new MemoryHeapBase(picture_width * 2 * picture_height); + mRawHeap = new MemoryHeapBase(picture_width * picture_height * 3 / 2); int preview_width, preview_height; mParameters.getPreviewSize(&preview_width, &preview_height); LOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height); - // Note that we enforce yuv422 in setParameters(). - int how_big = preview_width * preview_height * 2; + // Note that we enforce yuv420sp in setParameters(). + int how_big = preview_width * preview_height * 3 / 2; // If we are being reinitialized to the same size as before, no // work needs to be done. @@ -99,7 +99,6 @@ CameraHardwareStub::~CameraHardwareStub() { delete mFakeCamera; mFakeCamera = 0; // paranoia - singleton.clear(); } sp<IMemoryHeap> CameraHardwareStub::getPreviewHeap() const @@ -175,7 +174,7 @@ int CameraHardwareStub::previewThread() // Fill the current frame with the fake camera. uint8_t *frame = ((uint8_t *)base) + offset; - fakeCamera->getNextFrameAsYuv422(frame); + fakeCamera->getNextFrameAsYuv420(frame); //LOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame); @@ -288,9 +287,9 @@ int CameraHardwareStub::pictureThread() // In the meantime just make another fake camera picture. int w, h; mParameters.getPictureSize(&w, &h); - sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * 2 * h); + sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * h * 3 / 2); FakeCamera cam(w, h); - cam.getNextFrameAsYuv422((uint8_t *)mRawHeap->base()); + cam.getNextFrameAsYuv420((uint8_t *)mRawHeap->base()); mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie); } @@ -307,7 +306,7 @@ status_t CameraHardwareStub::takePicture() { stopPreview(); if (createThread(beginPictureThread, this) == false) - return -1; + return UNKNOWN_ERROR; return NO_ERROR; } @@ -339,12 +338,14 @@ status_t CameraHardwareStub::setParameters(const CameraParameters& params) Mutex::Autolock lock(mLock); // XXX verify params - if (strcmp(params.getPreviewFormat(), "yuv422sp") != 0) { - LOGE("Only yuv422sp preview is supported"); + if (strcmp(params.getPreviewFormat(), + CameraParameters::PIXEL_FORMAT_YUV420SP) != 0) { + LOGE("Only yuv420sp preview is supported"); return -1; } - if (strcmp(params.getPictureFormat(), "jpeg") != 0) { + if (strcmp(params.getPictureFormat(), + CameraParameters::PIXEL_FORMAT_JPEG) != 0) { LOGE("Only jpeg still pictures are supported"); return -1; } @@ -379,22 +380,12 @@ void CameraHardwareStub::release() { } -wp<CameraHardwareInterface> CameraHardwareStub::singleton; - sp<CameraHardwareInterface> CameraHardwareStub::createInstance() { - if (singleton != 0) { - sp<CameraHardwareInterface> hardware = singleton.promote(); - if (hardware != 0) { - return hardware; - } - } - sp<CameraHardwareInterface> hardware(new CameraHardwareStub()); - singleton = hardware; - return hardware; + return new CameraHardwareStub(); } -extern "C" sp<CameraHardwareInterface> openCameraHardware() +extern "C" sp<CameraHardwareInterface> openCameraHardwareStub() { return CameraHardwareStub::createInstance(); } diff --git a/camera/libcameraservice/CameraHardwareStub.h b/camera/libcameraservice/CameraHardwareStub.h index 957813a..d194f3c 100644 --- a/camera/libcameraservice/CameraHardwareStub.h +++ b/camera/libcameraservice/CameraHardwareStub.h @@ -67,8 +67,6 @@ private: CameraHardwareStub(); virtual ~CameraHardwareStub(); - static wp<CameraHardwareInterface> singleton; - static const int kBufferCount = 4; class PreviewThread : public Thread { @@ -130,6 +128,8 @@ private: int mCurrentPreviewFrame; }; +extern "C" sp<CameraHardwareInterface> openCameraHardwareStub(); + }; // namespace android #endif diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index 00bd54e..4f684b7 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 bc49b1d..86986ca 100644 --- a/camera/libcameraservice/CameraService.h +++ b/camera/libcameraservice/CameraService.h @@ -21,207 +21,171 @@ #include <camera/ICameraService.h> #include <camera/CameraHardwareInterface.h> -#include <camera/Camera.h> + +/* This needs to be increased if we can have more cameras */ +#define MAX_CAMERAS 2 namespace android { class MemoryHeapBase; class MediaPlayer; -// ---------------------------------------------------------------------------- - -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) - -// When enabled, this feature allows you to send an event to the CameraService -// so that you can cause all references to the heap object gWeakHeap, defined -// below, to be printed. You will also need to set DEBUG_REFS=1 and -// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp. You just have to -// set gWeakHeap to the appropriate heap you want to track. - -#define DEBUG_HEAP_LEAKS 0 - -// ---------------------------------------------------------------------------- - -class CameraService : public BnCameraService +class CameraService: public BnCameraService { class Client; - public: - static void instantiate(); + static void instantiate(); + + CameraService(); + virtual ~CameraService(); - // ICameraService interface - virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient); + virtual int32_t getNumberOfCameras(); + virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId); + virtual void removeClient(const sp<ICameraClient>& cameraClient); + virtual sp<Client> getClientById(int cameraId); - virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); - void removeClient(const sp<ICameraClient>& cameraClient); + enum sound_kind { + SOUND_SHUTTER = 0, + SOUND_RECORDING = 1, + NUM_SOUNDS + }; - virtual status_t onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + void loadSound(); + void playSound(sound_kind kind); + void releaseSound(); private: + Mutex mServiceLock; + wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock -// ---------------------------------------------------------------------------- + // atomics to record whether the hardware is allocated to some client. + volatile int32_t mBusy[MAX_CAMERAS]; + void setCameraBusy(int cameraId); + void setCameraFree(int cameraId); - class Client : public BnCamera { + // sounds + Mutex mSoundLock; + sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS]; + int mSoundRef; // reference count (release all MediaPlayer when 0) + class Client : public BnCamera + { public: + // ICamera interface (see ICamera for details) virtual void disconnect(); - - // connect new client with existing camera remote virtual status_t connect(const sp<ICameraClient>& client); - - // prevent other processes from using this ICamera interface virtual status_t lock(); - - // allow other processes to use this ICamera interface virtual status_t unlock(); - - // pass the buffered ISurface to the camera service virtual status_t setPreviewDisplay(const sp<ISurface>& surface); - - // set the preview callback flag to affect how the received frames from - // preview are handled. - virtual void setPreviewCallbackFlag(int callback_flag); - - // start preview mode, must call setPreviewDisplay first + virtual void setPreviewCallbackFlag(int flag); virtual status_t startPreview(); - - // stop preview mode virtual void stopPreview(); - - // get preview state virtual bool previewEnabled(); - - // start recording mode virtual status_t startRecording(); - - // stop recording mode virtual void stopRecording(); - - // get recording state virtual bool recordingEnabled(); - - // release a recording frame virtual void releaseRecordingFrame(const sp<IMemory>& mem); - - // auto focus virtual status_t autoFocus(); - - // cancel auto focus virtual status_t cancelAutoFocus(); - - // take a picture - returns an IMemory (ref-counted mmap) virtual status_t takePicture(); - - // set preview/capture parameters - key/value pairs virtual status_t setParameters(const String8& params); - - // get preview/capture parameters - key/value pairs virtual String8 getParameters() const; - - // send command to camera driver virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2); - - // our client... - const sp<ICameraClient>& getCameraClient() const { return mCameraClient; } - private: friend class CameraService; Client(const sp<CameraService>& cameraService, - const sp<ICameraClient>& cameraClient, - pid_t clientPid); - Client(); - virtual ~Client(); - - status_t checkPid(); - - static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user); - static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user); - static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, - const sp<IMemory>& dataPtr, void* user); + const sp<ICameraClient>& cameraClient, + int cameraId, + int clientPid); + ~Client(); - static sp<Client> getClientFromCookie(void* user); + // return our camera client + const sp<ICameraClient>& getCameraClient() { return mCameraClient; } - void handlePreviewData(const sp<IMemory>&); - void handleShutter(image_rect_type *image); - void handlePostview(const sp<IMemory>&); - void handleRawPicture(const sp<IMemory>&); - void handleCompressedPicture(const sp<IMemory>&); + // check whether the calling process matches mClientPid. + status_t checkPid() const; + status_t checkPidAndHardware() const; // also check mHardware != 0 - void copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client, - const sp<IMemoryHeap>& heap, size_t offset, size_t size); + // these are internal functions used to set up preview buffers + status_t registerPreviewBuffers(); + status_t setOverlay(); // camera operation mode enum camera_mode { CAMERA_PREVIEW_MODE = 0, // frame automatically released CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame() }; + // these are internal functions used for preview/recording status_t startCameraMode(camera_mode mode); status_t startPreviewMode(); status_t startRecordingMode(); - status_t setOverlay(); - status_t registerPreviewBuffers(); + + // these are static callback functions + static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user); + static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user); + static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user); + // convert client from cookie + static sp<Client> getClientFromCookie(void* user); + // handlers for messages + void handleShutter(image_rect_type *size); + void handlePreviewData(const sp<IMemory>& mem); + void handlePostview(const sp<IMemory>& mem); + void handleRawPicture(const sp<IMemory>& mem); + void handleCompressedPicture(const sp<IMemory>& mem); + void handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2); + void handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr); + void handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr); + + void copyFrameAndPostCopiedFrame( + const sp<ICameraClient>& client, + const sp<IMemoryHeap>& heap, + size_t offset, size_t size); + + // these are initialized in the constructor. + sp<CameraService> mCameraService; // immutable after constructor + sp<ICameraClient> mCameraClient; + int mCameraId; // immutable after constructor + pid_t mClientPid; + sp<CameraHardwareInterface> mHardware; // cleared after disconnect() + bool mUseOverlay; // immutable after constructor + sp<OverlayRef> mOverlayRef; + int mOverlayW; + int mOverlayH; + int mPreviewCallbackFlag; + int mOrientation; // Ensures atomicity among the public methods - mutable Mutex mLock; - - // mSurfaceLock synchronizes access to mSurface between - // setPreviewSurface() and postPreviewFrame(). Note that among - // the public methods, all accesses to mSurface are - // syncrhonized by mLock. However, postPreviewFrame() is called - // by the CameraHardwareInterface callback, and needs to - // access mSurface. It cannot hold mLock, however, because - // stopPreview() may be holding that lock while attempting - // to stop preview, and stopPreview itself will block waiting - // for a callback from CameraHardwareInterface. If this - // happens, it will cause a deadlock. - mutable Mutex mSurfaceLock; - mutable Condition mReady; - sp<CameraService> mCameraService; - sp<ISurface> mSurface; - int mPreviewCallbackFlag; - int mOrientation; - - sp<MediaPlayer> mMediaPlayerClick; - sp<MediaPlayer> mMediaPlayerBeep; - - // these are immutable once the object is created, - // they don't need to be protected by a lock - sp<ICameraClient> mCameraClient; - sp<CameraHardwareInterface> mHardware; - pid_t mClientPid; - bool mUseOverlay; - - sp<OverlayRef> mOverlayRef; - int mOverlayW; - int mOverlayH; - - mutable Mutex mPreviewLock; - sp<MemoryHeapBase> mPreviewBuffer; + mutable Mutex mLock; + sp<ISurface> mSurface; + + // If the user want us to return a copy of the preview frame (instead + // of the original one), we allocate mPreviewBuffer and reuse it if possible. + sp<MemoryHeapBase> mPreviewBuffer; + + // We need to avoid the deadlock when the incoming command thread and + // the CameraHardwareInterface callback thread both want to grab mLock. + // An extra flag is used to tell the callback thread that it should stop + // trying to deliver the callback messages if the client is not + // interested in it anymore. For example, if the client is calling + // stopPreview(), the preview frame messages do not need to be delivered + // anymore. + + // This function takes the same parameter as the enableMsgType() and + // disableMsgType() functions in CameraHardwareInterface. + void enableMsgType(int32_t msgType); + void disableMsgType(int32_t msgType); + volatile int32_t mMsgEnabled; + + // This function keeps trying to grab mLock, or give up if the message + // is found to be disabled. It returns true if mLock is grabbed. + bool lockIfMessageWanted(int32_t msgType); }; - -// ---------------------------------------------------------------------------- - - CameraService(); - virtual ~CameraService(); - - // We use a count for number of clients (shoule only be 0 or 1). - volatile int32_t mUsers; - virtual void incUsers(); - virtual void decUsers(); - - mutable Mutex mServiceLock; - wp<Client> mClient; - -#if DEBUG_HEAP_LEAKS - wp<IMemoryHeap> gWeakHeap; -#endif }; -// ---------------------------------------------------------------------------- - -}; // namespace android +} // namespace android #endif diff --git a/camera/libcameraservice/FakeCamera.cpp b/camera/libcameraservice/FakeCamera.cpp index 6749899..f3a6a67 100644 --- a/camera/libcameraservice/FakeCamera.cpp +++ b/camera/libcameraservice/FakeCamera.cpp @@ -198,10 +198,11 @@ static const int SHIFT2 = 16; static const int DELTA = kYb*(1 << SHIFT2); static const int GAMMA = kYr*(1 << SHIFT2); -int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16,uint8_t *yuv422,uint32_t *param,uint8_t *table[]) +int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16, uint8_t *yuv420, + uint32_t *param, uint8_t *table[]) { uint16_t *inputRGB = (uint16_t*)rgb16; - uint8_t *outYUV = yuv422; + uint8_t *outYUV = yuv420; int32_t width_dst = param[0]; int32_t height_dst = param[1]; int32_t pitch_dst = param[2]; @@ -260,12 +261,14 @@ uint32_t temp; tempY[0] = y0; tempY[1] = y1; - tempU[0] = u; - tempV[0] = v; - tempY += 2; - tempU += 2; - tempV += 2; + + if ((j&1) == 0) { + tempU[0] = u; + tempV[0] = v; + tempU += 2; + tempV += 2; + } } inputRGB += pitch_src; @@ -277,7 +280,7 @@ uint32_t temp; #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) -static void convert_rgb16_to_yuv422(uint8_t *rgb, uint8_t *yuv, int width, int height) +static void convert_rgb16_to_yuv420(uint8_t *rgb, uint8_t *yuv, int width, int height) { if (!tables_initialized) { initYtab(); @@ -326,7 +329,7 @@ void FakeCamera::setSize(int width, int height) mCheckY = 0; // This will cause it to be reallocated on the next call - // to getNextFrameAsYuv422(). + // to getNextFrameAsYuv420(). delete[] mTmpRgb16Buffer; mTmpRgb16Buffer = 0; } @@ -347,13 +350,13 @@ void FakeCamera::getNextFrameAsRgb565(uint16_t *buffer) mCounter++; } -void FakeCamera::getNextFrameAsYuv422(uint8_t *buffer) +void FakeCamera::getNextFrameAsYuv420(uint8_t *buffer) { if (mTmpRgb16Buffer == 0) mTmpRgb16Buffer = new uint16_t[mWidth * mHeight]; getNextFrameAsRgb565(mTmpRgb16Buffer); - convert_rgb16_to_yuv422((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight); + convert_rgb16_to_yuv420((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight); } void FakeCamera::drawSquare(uint16_t *dst, int x, int y, int size, int color, int shadow) diff --git a/camera/libcameraservice/FakeCamera.h b/camera/libcameraservice/FakeCamera.h index f7f8803..724de20 100644 --- a/camera/libcameraservice/FakeCamera.h +++ b/camera/libcameraservice/FakeCamera.h @@ -40,7 +40,7 @@ public: ~FakeCamera(); void setSize(int width, int height); - void getNextFrameAsYuv422(uint8_t *buffer); + void getNextFrameAsYuv420(uint8_t *buffer); // Write to the fd a string representing the current state. void dump(int fd) const; diff --git a/camera/tests/CameraServiceTest/Android.mk b/camera/tests/CameraServiceTest/Android.mk index 9bb190a..cf4e42f 100644 --- a/camera/tests/CameraServiceTest/Android.mk +++ b/camera/tests/CameraServiceTest/Android.mk @@ -21,4 +21,6 @@ LOCAL_SHARED_LIBRARIES += \ libcamera_client \ libsurfaceflinger_client -include $(BUILD_EXECUTABLE) +# Disable it because the ISurface interface may change, and before we have a +# chance to fix this test, we don't want to break normal builds. +#include $(BUILD_EXECUTABLE) diff --git a/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/camera/tests/CameraServiceTest/CameraServiceTest.cpp index 9fc795b..3c8d553 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,26 +200,33 @@ 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) { - INFO(__func__); + INFO("%s", __func__); Mutex::Autolock _l(mLock); ssize_t i = mNotifyCount.indexOfKey(msgType); if (i < 0) { @@ -230,7 +238,7 @@ void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) } void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) { - INFO(__func__); + INFO("%s", __func__); int dataSize = data->size(); INFO("data type = %d, size = %d", msgType, dataSize); Mutex::Autolock _l(mLock); @@ -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); @@ -285,6 +298,7 @@ public: virtual sp<OverlayRef> createOverlay( uint32_t w, uint32_t h, int32_t format, int32_t orientation); virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage); + virtual status_t setBufferCount(int bufferCount); // new functions void clearStat(); @@ -300,7 +314,7 @@ private: }; status_t MSurface::registerBuffers(const BufferHeap& buffers) { - INFO(__func__); + INFO("%s", __func__); Mutex::Autolock _l(mLock); ++registerBuffersCount; mCond.signal(); @@ -308,21 +322,26 @@ status_t MSurface::registerBuffers(const BufferHeap& buffers) { } void MSurface::postBuffer(ssize_t offset) { - // INFO(__func__); + // INFO("%s", __func__); Mutex::Autolock _l(mLock); ++postBufferCount; mCond.signal(); } void MSurface::unregisterBuffers() { - INFO(__func__); + INFO("%s", __func__); Mutex::Autolock _l(mLock); ++unregisterBuffersCount; mCond.signal(); } sp<GraphicBuffer> MSurface::requestBuffer(int bufferIdx, int usage) { - INFO(__func__); + INFO("%s", __func__); + return NULL; +} + +status_t MSurface::setBufferCount(int bufferCount) { + INFO("%s", __func__); return NULL; } @@ -348,10 +367,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; } // @@ -368,17 +386,17 @@ sp<IHolder> getHolder() { } void putTempObject(sp<IBinder> obj) { - INFO(__func__); + INFO("%s", __func__); getHolder()->put(obj); } sp<IBinder> getTempObject() { - INFO(__func__); + INFO("%s", __func__); return getHolder()->get(); } void clearTempObject() { - INFO(__func__); + INFO("%s", __func__); getHolder()->clear(); } @@ -395,64 +413,71 @@ sp<ICameraService> getCameraService() { return cs; } +int getNumberOfCameras() { + sp<ICameraService> cs = getCameraService(); + return cs->getNumberOfCameras(); +} + // // Various Connect Tests // -void testConnect() { - INFO(__func__); +void testConnect(int cameraId) { + INFO("%s", __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() { - INFO(__func__); +void testAllowConnectOnceOnly(int cameraId) { + INFO("%s", __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__); + INFO("%s", __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() { - INFO(__func__); + INFO("%s", __func__); sp<ICamera> c = interface_cast<ICamera>(getTempObject()); sp<MCameraClient> cc = new MCameraClient(); ASSERT(c->connect(cc) == NO_ERROR); + c->disconnect(); } void testLockFailed() { - INFO(__func__); + INFO("%s", __func__); sp<ICamera> c = interface_cast<ICamera>(getTempObject()); ASSERT(c->lock() != NO_ERROR); } void testLockUnlockSuccess() { - INFO(__func__); + INFO("%s", __func__); sp<ICamera> c = interface_cast<ICamera>(getTempObject()); ASSERT(c->lock() == NO_ERROR); ASSERT(c->unlock() == NO_ERROR); } void testLockSuccess() { - INFO(__func__); + INFO("%s", __func__); sp<ICamera> c = interface_cast<ICamera>(getTempObject()); ASSERT(c->lock() == NO_ERROR); + c->disconnect(); } // @@ -499,11 +524,11 @@ void runInAnotherProcess(const char *tag) { } } -void testReconnect() { - INFO(__func__); +void testReconnect(int cameraId) { + INFO("%s", __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 +539,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 +555,15 @@ void testLockUnlock() { runInAnotherProcess("testLockUnlockSuccess"); // Unlock then lock from a different process -- ok. runInAnotherProcess("testLockSuccess"); - c->disconnect(); clearTempObject(); } -void testReconnectFromAnotherProcess() { - INFO(__func__); +void testReconnectFromAnotherProcess(int cameraId) { + INFO("%s", __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 +571,6 @@ void testReconnectFromAnotherProcess() { // Unlock then reconnect from a different process -- ok. ASSERT(c->unlock() == NO_ERROR); runInAnotherProcess("testReconnectSuccess"); - c->disconnect(); clearTempObject(); } @@ -560,10 +583,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 +595,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 +638,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 +659,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 +705,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 +719,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); @@ -708,36 +729,71 @@ class TestGetParameters: public AfterStartPreview { public: void run() { String8 param_str = c->getParameters(); - INFO(param_str); + INFO("%s", static_cast<const char*>(param_str)); } }; +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 +805,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 +839,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 +865,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 +890,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 9b5a1e0..dcce25e 100644 --- a/include/private/surfaceflinger/SharedBufferStack.h +++ b/include/private/surfaceflinger/SharedBufferStack.h @@ -55,12 +55,6 @@ 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_DISPLAY_MAX = 4; - // ---------------------------------------------------------------------------- class Region; @@ -69,7 +63,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 +76,38 @@ 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]; - }; - + // When changing these values, the COMPILE_TIME_ASSERT at the end of this + // file need to be updated. + static const unsigned int NUM_LAYERS_MAX = 31; + static const unsigned int NUM_BUFFER_MAX = 16; + static const unsigned int NUM_BUFFER_MIN = 2; + static const unsigned int NUM_DISPLAY_MAX = 4; + 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,24 +119,24 @@ 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 }; // ---------------------------------------------------------------------------- -// 4 KB max +// 32 KB max class SharedClient { public: SharedClient(); ~SharedClient(); - status_t validate(size_t token) const; - uint32_t getIdentity(size_t token) const; private: friend class SharedBufferBase; @@ -131,7 +146,7 @@ private: // FIXME: this should be replaced by a lock-less primitive Mutex lock; Condition cv; - SharedBufferStack surfaces[ NUM_LAYERS_MAX ]; + SharedBufferStack surfaces[ SharedBufferStack::NUM_LAYERS_MAX ]; }; // ============================================================================ @@ -139,18 +154,17 @@ private: class SharedBufferBase { public: - SharedBufferBase(SharedClient* sharedClient, int surface, int num, + SharedBufferBase(SharedClient* sharedClient, int surface, int32_t identity); ~SharedBufferBase(); - uint32_t getIdentity(); status_t getStatus() const; + int32_t getIdentity() const; size_t getFrontBuffer() const; String8 dump(char const* prefix) const; protected: SharedClient* const mSharedClient; SharedBufferStack* const mSharedStack; - const int mNumBuffers; const int mIdentity; friend struct Update; @@ -160,61 +174,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 +213,21 @@ 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); + + class SetBufferCountCallback { + friend class SharedBufferClient; + virtual status_t operator()(int bufferCount) const = 0; + protected: + virtual ~SetBufferCountCallback() { } + }; + status_t setBufferCount(int bufferCount, const SetBufferCountCallback& ipc); + private: friend struct Condition; friend struct DequeueCondition; friend struct LockCondition; - - int32_t computeTail() const; struct QueueUpdate : public UpdateBase { inline QueueUpdate(SharedBufferBase* sbb); @@ -260,20 +243,27 @@ 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 computeTail() const; + + mutable RWLock mLock; + int mNumBuffers; + int32_t tail; + int32_t undoDequeueTail; + int32_t queued_head; // statistics... - nsecs_t mDequeueTime[NUM_BUFFER_MAX]; + nsecs_t mDequeueTime[SharedBufferStack::NUM_BUFFER_MAX]; }; // ---------------------------------------------------------------------------- @@ -287,16 +277,71 @@ public: ssize_t retireAndLock(); status_t unlock(int buffer); void setStatus(status_t status); - status_t reallocate(); + status_t reallocateAll(); + status_t reallocateAllExcept(int buffer); status_t assertReallocate(int buffer); int32_t getQueuedCount() const; - Region getDirtyRegion(int buffer) const; + status_t resize(int newNumBuffers); + SharedBufferStack::Statistics getStats() const; private: + /* + * BufferList is basically a fixed-capacity sorted-vector of + * unsigned 5-bits ints using a 32-bits int as storage. + * it has efficient iterators to find items in the list and not in the list. + */ + class BufferList { + size_t mCapacity; + uint32_t mList; + public: + BufferList(size_t c = SharedBufferStack::NUM_BUFFER_MAX) + : mCapacity(c), mList(0) { } + status_t add(int value); + status_t remove(int value); + uint32_t getMask() const { return mList; } + + class const_iterator { + friend class BufferList; + uint32_t mask, curr; + const_iterator(uint32_t mask) : + mask(mask), curr(__builtin_clz(mask)) { + } + public: + inline bool operator == (const const_iterator& rhs) const { + return mask == rhs.mask; + } + inline bool operator != (const const_iterator& rhs) const { + return mask != rhs.mask; + } + inline int operator *() const { return curr; } + inline const const_iterator& operator ++() { + mask &= ~(1<<(31-curr)); + curr = __builtin_clz(mask); + return *this; + } + }; + + inline const_iterator begin() const { + return const_iterator(mList); + } + inline const_iterator end() const { + return const_iterator(0); + } + inline const_iterator free_begin() const { + uint32_t mask = (1 << (32-mCapacity)) - 1; + return const_iterator( ~(mList | mask) ); + } + }; + + // this protects mNumBuffers and mBufferList + mutable RWLock mLock; + int mNumBuffers; + BufferList mBufferList; + struct UnlockUpdate : public UpdateBase { const int lockedBuffer; inline UnlockUpdate(SharedBufferBase* sbb, int lockedBuffer); @@ -318,8 +363,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"; } }; }; @@ -344,13 +389,12 @@ struct surface_flinger_cblk_t // 4KB max uint8_t connected; uint8_t reserved[3]; uint32_t pad[7]; - display_cblk_t displays[NUM_DISPLAY_MAX]; + display_cblk_t displays[SharedBufferStack::NUM_DISPLAY_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/ISurface.h b/include/surfaceflinger/ISurface.h index 472f759..ddbe03d 100644 --- a/include/surfaceflinger/ISurface.h +++ b/include/surfaceflinger/ISurface.h @@ -47,13 +47,30 @@ protected: POST_BUFFER, // one-way transaction CREATE_OVERLAY, REQUEST_BUFFER, + SET_BUFFER_COUNT, }; public: DECLARE_META_INTERFACE(Surface); - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) = 0; + /* + * requests a new buffer for the given index. If w, h, or format are + * null the buffer is created with the parameters assigned to the + * surface it is bound to. Otherwise the buffer's parameters are + * set to those specified. + */ + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) = 0; + + /* + * sets the number of buffers dequeuable for this surface. + */ + virtual status_t setBufferCount(int bufferCount) = 0; + // ------------------------------------------------------------------------ + // Deprecated... + // ------------------------------------------------------------------------ + class BufferHeap { public: enum { diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h index d1e7785..3271cfd 100644 --- a/include/surfaceflinger/ISurfaceComposer.h +++ b/include/surfaceflinger/ISurfaceComposer.h @@ -27,7 +27,7 @@ #include <ui/PixelFormat.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> namespace android { // ---------------------------------------------------------------------------- @@ -86,7 +86,7 @@ public: * ACCESS_SURFACE_FLINGER permission */ - virtual sp<ISurfaceFlingerClient> createConnection() = 0; + virtual sp<ISurfaceComposerClient> createConnection() = 0; /* retrieve the control block */ virtual sp<IMemoryHeap> getCblk() const = 0; diff --git a/include/surfaceflinger/ISurfaceFlingerClient.h b/include/surfaceflinger/ISurfaceComposerClient.h index d257645..b2a4766 100644 --- a/include/surfaceflinger/ISurfaceFlingerClient.h +++ b/include/surfaceflinger/ISurfaceComposerClient.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_SF_ISURFACE_FLINGER_CLIENT_H -#define ANDROID_SF_ISURFACE_FLINGER_CLIENT_H +#ifndef ANDROID_SF_ISURFACE_COMPOSER_CLIENT_H +#define ANDROID_SF_ISURFACE_COMPOSER_CLIENT_H #include <stdint.h> #include <sys/types.h> @@ -26,7 +26,7 @@ #include <binder/IInterface.h> #include <ui/PixelFormat.h> - + #include <surfaceflinger/ISurface.h> namespace android { @@ -42,10 +42,10 @@ typedef int32_t DisplayID; class layer_state_t; -class ISurfaceFlingerClient : public IInterface +class ISurfaceComposerClient : public IInterface { -public: - DECLARE_META_INTERFACE(SurfaceFlingerClient); +public: + DECLARE_META_INTERFACE(SurfaceComposerClient); struct surface_data_t { int32_t token; @@ -56,26 +56,35 @@ public: status_t readFromParcel(const Parcel& parcel); status_t writeToParcel(Parcel* parcel) const; }; - + virtual sp<IMemoryHeap> getControlBlock() const = 0; + /* + * Requires ACCESS_SURFACE_FLINGER permission + */ virtual sp<ISurface> createSurface( surface_data_t* data, - int pid, + int pid, const String8& name, DisplayID display, uint32_t w, uint32_t h, 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; }; // ---------------------------------------------------------------------------- -class BnSurfaceFlingerClient : public BnInterface<ISurfaceFlingerClient> +class BnSurfaceComposerClient : public BnInterface<ISurfaceComposerClient> { public: virtual status_t onTransact( uint32_t code, @@ -88,4 +97,4 @@ public: }; // namespace android -#endif // ANDROID_SF_ISURFACE_FLINGER_CLIENT_H +#endif // ANDROID_SF_ISURFACE_COMPOSER_CLIENT_H diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h index 0279d84..2957970 100644 --- a/include/surfaceflinger/Surface.h +++ b/include/surfaceflinger/Surface.h @@ -28,12 +28,15 @@ #include <ui/egl/android_natives.h> #include <surfaceflinger/ISurface.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> + +#define ANDROID_VIEW_SURFACE_JNI_ID "mNativeSurface" namespace android { // --------------------------------------------------------------------------- +class GraphicBuffer; class GraphicBufferMapper; class IOMX; class Rect; @@ -41,6 +44,7 @@ class Surface; class SurfaceComposerClient; class SharedClient; class SharedBufferClient; +class SurfaceClient; // --------------------------------------------------------------------------- @@ -104,7 +108,7 @@ private: SurfaceControl( const sp<SurfaceComposerClient>& client, const sp<ISurface>& surface, - const ISurfaceFlingerClient::surface_data_t& data, + const ISurfaceComposerClient::surface_data_t& data, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags); ~SurfaceControl(); @@ -163,38 +167,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,21 +210,61 @@ 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); + int dispatch_set_buffer_count(va_list args); + int dispatch_set_buffers_geometry(va_list args); void setUsage(uint32_t reqUsage); int connect(int api); int disconnect(int api); + int crop(Rect const* rect); + int setBufferCount(int bufferCount); + int setBuffersGeometry(int w, int h, int format); - uint32_t getUsage() const; - int getConnectedApi() const; + /* + * private stuff... + */ + void init(); + status_t validate() const; + status_t initCheck() const; + sp<ISurface> getISurface() const; + + inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; } + inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; } + + status_t getBufferLocked(int index, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + int getBufferIndex(const sp<GraphicBuffer>& buffer) const; + + int getConnectedApi() const; + bool needNewBuffer(int bufIdx, + uint32_t *pWidth, uint32_t *pHeight, + uint32_t *pFormat, uint32_t *pUsage) const; + + class BufferInfo { + uint32_t mWidth; + uint32_t mHeight; + uint32_t mFormat; + uint32_t mUsage; + mutable uint32_t mDirty; + enum { + GEOMETRY = 0x01 + }; + public: + BufferInfo(); + void set(uint32_t w, uint32_t h, uint32_t format); + void set(uint32_t usage); + void get(uint32_t *pWidth, uint32_t *pHeight, + uint32_t *pFormat, uint32_t *pUsage) const; + bool validateBuffer(const sp<GraphicBuffer>& buffer) const; + }; + // constants - sp<SurfaceComposerClient> mClient; + sp<SurfaceClient> mClient; sp<ISurface> mSurface; SurfaceID mToken; uint32_t mIdentity; @@ -230,22 +272,26 @@ private: uint32_t mFlags; GraphicBufferMapper& mBufferMapper; SharedBufferClient* mSharedBufferClient; + status_t mInitCheck; // protected by mSurfaceLock Rect mSwapRectangle; - uint32_t mUsage; int mConnected; + Rect mNextBufferCrop; + BufferInfo mBufferInfo; // protected by mSurfaceLock. These are also used from lock/unlock // but in that case, they must be called form the same thread. - sp<GraphicBuffer> mBuffers[2]; mutable Region mDirtyRegion; // must be used from the lock/unlock thread sp<GraphicBuffer> mLockedBuffer; sp<GraphicBuffer> mPostedBuffer; mutable Region mOldDirtyRegion; - bool mNeedFullUpdate; + bool mReserved; + + // only used from dequeueBuffer() + Vector< sp<GraphicBuffer> > mBuffers; // query() must be called from dequeueBuffer() thread uint32_t mWidth; diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h index 9d0f0cb..8e28a81 100644 --- a/include/surfaceflinger/SurfaceComposerClient.h +++ b/include/surfaceflinger/SurfaceComposerClient.h @@ -40,7 +40,9 @@ class SharedClient; class ISurfaceComposer; class DisplayInfo; -class SurfaceComposerClient : virtual public RefBase +// --------------------------------------------------------------------------- + +class SurfaceComposerClient : public RefBase { public: SurfaceComposerClient(); @@ -52,10 +54,6 @@ public: // Return the connection of this client sp<IBinder> connection() const; - // Retrieve a client for an existing connection. - static sp<SurfaceComposerClient> - clientForConnection(const sp<IBinder>& conn); - // Forcibly remove connection before all references have gone away. void dispose(); @@ -123,13 +121,6 @@ public: status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient, void* cookie = NULL, uint32_t flags = 0); -private: - friend class Surface; - friend class SurfaceControl; - - SurfaceComposerClient(const sp<ISurfaceComposer>& sm, - const sp<IBinder>& conn); - status_t hide(SurfaceID id); status_t show(SurfaceID id, int32_t layer = -1); status_t freeze(SurfaceID id); @@ -142,32 +133,45 @@ private: status_t setMatrix(SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy); status_t setPosition(SurfaceID id, int32_t x, int32_t y); status_t setSize(SurfaceID id, uint32_t w, uint32_t h); - - void signalServer(); - status_t destroySurface(SurfaceID sid); - void _init(const sp<ISurfaceComposer>& sm, - const sp<ISurfaceFlingerClient>& conn); - - inline layer_state_t* _get_state_l(SurfaceID id); - layer_state_t* _lockLayerState(SurfaceID id); - inline void _unlockLayerState(); +private: + virtual void onFirstRef(); + inline layer_state_t* get_state_l(SurfaceID id); + layer_state_t* lockLayerState(SurfaceID id); + inline void unlockLayerState(); mutable Mutex mLock; - layer_state_t* mPrebuiltLayerState; SortedVector<layer_state_t> mStates; int32_t mTransactionOpen; + layer_state_t* mPrebuiltLayerState; // these don't need to be protected because they never change // after assignment status_t mStatus; - SharedClient* mControl; - sp<IMemoryHeap> mControlMemory; - sp<ISurfaceFlingerClient> mClient; - sp<ISurfaceComposer> mSignalServer; + sp<ISurfaceComposerClient> mClient; }; +// --------------------------------------------------------------------------- + +class SurfaceClient : public RefBase +{ + // all these attributes are constants + status_t mStatus; + SharedClient* mControl; + sp<IMemoryHeap> mControlMemory; + sp<IBinder> mConnection; + sp<ISurfaceComposer> mComposerService; + void init(const sp<IBinder>& conn); +public: + explicit SurfaceClient(const sp<IBinder>& conn); + explicit SurfaceClient(const sp<SurfaceComposerClient>& client); + status_t initCheck() const; + SharedClient* getSharedClient() const; + void signalServer() const; +}; + +// --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_SF_SURFACE_COMPOSER_CLIENT_H diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h index 741d763..54b8236 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/android_native_buffer.h b/include/ui/android_native_buffer.h index 9c92af8..402843e 100644 --- a/include/ui/android_native_buffer.h +++ b/include/ui/android_native_buffer.h @@ -33,6 +33,15 @@ typedef struct android_native_buffer_t common.version = sizeof(android_native_buffer_t); memset(common.reserved, 0, sizeof(common.reserved)); } + + // Implement the methods that sp<android_native_buffer_t> expects so that it + // can be used to automatically refcount android_native_buffer_t's. + void incStrong(const void* id) const { + common.incRef(const_cast<android_native_base_t*>(&common)); + } + void decStrong(const void* id) const { + common.decRef(const_cast<android_native_base_t*>(&common)); + } #endif struct android_native_base_t common; diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h index 773fd93..171f3df 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,18 @@ 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, + NATIVE_WINDOW_SET_BUFFER_COUNT, + NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, }; /* parameter for NATIVE_WINDOW_[DIS]CONNECT */ @@ -89,6 +100,15 @@ typedef struct android_native_window_t common.version = sizeof(android_native_window_t); memset(common.reserved, 0, sizeof(common.reserved)); } + + // Implement the methods that sp<android_native_window_t> expects so that it + // can be used to automatically refcount android_native_window_t's. + void incStrong(const void* id) const { + common.incRef(const_cast<android_native_base_t*>(&common)); + } + void decStrong(const void* id) const { + common.decRef(const_cast<android_native_base_t*>(&common)); + } #endif struct android_native_base_t common; @@ -125,7 +145,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 +191,9 @@ typedef struct android_native_window_t * NATIVE_WINDOW_SET_USAGE * NATIVE_WINDOW_CONNECT * NATIVE_WINDOW_DISCONNECT + * NATIVE_WINDOW_SET_CROP + * NATIVE_WINDOW_SET_BUFFER_COUNT + * NATIVE_WINDOW_SET_BUFFERS_GEOMETRY * */ @@ -182,8 +205,9 @@ typedef struct android_native_window_t /* - * native_window_set_usage() sets the intended usage flags for the next - * buffers acquired with (*lockBuffer)() and on. + * native_window_set_usage(..., usage) + * Sets the intended usage flags for the next buffers + * acquired with (*lockBuffer)() and on. * By default (if this function is never called), a usage of * GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE * is assumed. @@ -198,8 +222,8 @@ static inline int native_window_set_usage( } /* - * native_window_connect(..., NATIVE_WINDOW_API_EGL) must be called - * by EGL when the window is made current. + * native_window_connect(..., NATIVE_WINDOW_API_EGL) + * Must be called by EGL when the window is made current. * Returns -EINVAL if for some reason the window cannot be connected, which * can happen if it's connected to some other API. */ @@ -210,8 +234,8 @@ static inline int native_window_connect( } /* - * native_window_disconnect(..., NATIVE_WINDOW_API_EGL) must be called - * by EGL when the window is made not current. + * native_window_disconnect(..., NATIVE_WINDOW_API_EGL) + * Must be called by EGL when the window is made not current. * An error is returned if for instance the window wasn't connected in the * first place. */ @@ -221,6 +245,54 @@ 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); +} + +/* + * native_window_set_buffer_count(..., count) + * Sets the number of buffers associated with this native window. + */ +static inline int native_window_set_buffer_count( + android_native_window_t* window, + size_t bufferCount) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount); +} + +/* + * native_window_set_buffers_geometry(..., int w, int h, int format) + * All buffers dequeued after this call will have the geometry specified. + * In particular, all buffers will have a fixed-size, independent form the + * native-window size. They will be appropriately scaled to the window-size + * upon composition. + * + * If all parameters are 0, the normal behavior is restored. That is, + * dequeued buffers following this call will be sized to the window's size. + * + */ +static inline int native_window_set_buffers_geometry( + android_native_window_t* window, + int w, int h, int format) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, + w, h, format); +} // --------------------------------------------------------------------------- @@ -263,6 +335,15 @@ namespace android { template <typename NATIVE_TYPE, typename TYPE, typename REF> class EGLNativeBase : public NATIVE_TYPE, public REF { +public: + // Disambiguate between the incStrong in REF and NATIVE_TYPE + void incStrong(const void* id) const { + REF::incStrong(id); + } + void decStrong(const void* id) const { + REF::decStrong(id); + } + protected: typedef EGLNativeBase<NATIVE_TYPE, TYPE, REF> BASE; EGLNativeBase() : NATIVE_TYPE(), REF() { diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index b701ce7..c7d9ff1 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/include/utils/Singleton.h b/include/utils/Singleton.h index bc7626a..3b975b4 100644 --- a/include/utils/Singleton.h +++ b/include/utils/Singleton.h @@ -54,11 +54,13 @@ private: * (eg: <TYPE>.cpp) to create the static instance of Singleton<>'s attributes, * and avoid to have a copy of them in each compilation units Singleton<TYPE> * is used. + * NOTE: we use a version of Mutex ctor that takes a parameter, because + * for some unknown reason using the default ctor doesn't emit the variable! */ -#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \ - template class Singleton< TYPE >; \ - template< class TYPE > Mutex Singleton< TYPE >::sLock; \ +#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \ + template class Singleton< TYPE >; \ + template<> Mutex Singleton< TYPE >::sLock(Mutex::PRIVATE); \ template<> TYPE* Singleton< TYPE >::sInstance(0); diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h index 30e0036..e38bf66 100644 --- a/include/utils/ZipFileCRO.h +++ b/include/utils/ZipFileCRO.h @@ -47,8 +47,8 @@ extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, const char* fileName); extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, - int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); + int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index 51c4f2f..97d31f4 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -58,14 +58,19 @@ typedef void* ZipEntryRO; class ZipFileRO { public: ZipFileRO() - : mFd(-1), mFileMap(NULL), mHashTableSize(-1), mHashTable(NULL) + : mFd(-1), mFileName(NULL), mFileLength(-1), + mDirectoryMap(NULL), + mNumEntries(-1), mDirectoryOffset(-1), + mHashTableSize(-1), mHashTable(NULL) {} ~ZipFileRO() { free(mHashTable); - if (mFileMap) - mFileMap->release(); + if (mDirectoryMap) + mDirectoryMap->release(); if (mFd >= 0) close(mFd); + if (mFileName) + free(mFileName); } /* @@ -118,8 +123,8 @@ public: * Returns "false" if "entry" is bogus or if the data in the Zip file * appears to be bad. */ - bool getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; + bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; /* * Create a new FileMap object that maps a subset of the archive. For @@ -155,13 +160,13 @@ public: * Utility function: uncompress deflated data, buffer to buffer. */ static bool inflateBuffer(void* outBuf, const void* inBuf, - long uncompLen, long compLen); + size_t uncompLen, size_t compLen); /* * Utility function: uncompress deflated data, buffer to fd. */ static bool inflateBuffer(int fd, const void* inBuf, - long uncompLen, long compLen); + size_t uncompLen, size_t compLen); /* * Some basic functions for raw data manipulation. "LE" means @@ -179,6 +184,9 @@ private: ZipFileRO(const ZipFileRO& src); ZipFileRO& operator=(const ZipFileRO& src); + /* locate and parse the central directory */ + bool mapCentralDirectory(void); + /* parse the archive, prepping internal structures */ bool parseZipArchive(void); @@ -203,12 +211,21 @@ private: /* open Zip archive */ int mFd; + /* zip file name */ + char* mFileName; + + /* length of file */ + size_t mFileLength; + /* mapped file */ - FileMap* mFileMap; + FileMap* mDirectoryMap; /* number of entries in the Zip archive */ int mNumEntries; + /* CD directory offset in the Zip archive */ + off_t mDirectoryOffset; + /* * We know how many entries are in the Zip archive, so we have a * fixed-size hash table. We probe for an empty slot. diff --git a/include/utils/threads.h b/include/utils/threads.h index 5ac0c5e..1bcfaed 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -295,6 +295,96 @@ typedef Mutex::Autolock AutoMutex; /*****************************************************************************/ +#if defined(HAVE_PTHREADS) + +/* + * Simple mutex class. The implementation is system-dependent. + * + * The mutex must be unlocked by the thread that locked it. They are not + * recursive, i.e. the same thread can't lock it multiple times. + */ +class RWLock { +public: + enum { + PRIVATE = 0, + SHARED = 1 + }; + + RWLock(); + RWLock(const char* name); + RWLock(int type, const char* name = NULL); + ~RWLock(); + + status_t readLock(); + status_t tryReadLock(); + status_t writeLock(); + status_t tryWriteLock(); + void unlock(); + + class AutoRLock { + public: + inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); } + inline ~AutoRLock() { mLock.unlock(); } + private: + RWLock& mLock; + }; + + class AutoWLock { + public: + inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); } + inline ~AutoWLock() { mLock.unlock(); } + private: + RWLock& mLock; + }; + +private: + // A RWLock cannot be copied + RWLock(const RWLock&); + RWLock& operator = (const RWLock&); + + pthread_rwlock_t mRWLock; +}; + +inline RWLock::RWLock() { + pthread_rwlock_init(&mRWLock, NULL); +} +inline RWLock::RWLock(const char* name) { + pthread_rwlock_init(&mRWLock, NULL); +} +inline RWLock::RWLock(int type, const char* name) { + if (type == SHARED) { + pthread_rwlockattr_t attr; + pthread_rwlockattr_init(&attr); + pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_rwlock_init(&mRWLock, &attr); + pthread_rwlockattr_destroy(&attr); + } else { + pthread_rwlock_init(&mRWLock, NULL); + } +} +inline RWLock::~RWLock() { + pthread_rwlock_destroy(&mRWLock); +} +inline status_t RWLock::readLock() { + return -pthread_rwlock_rdlock(&mRWLock); +} +inline status_t RWLock::tryReadLock() { + return -pthread_rwlock_tryrdlock(&mRWLock); +} +inline status_t RWLock::writeLock() { + return -pthread_rwlock_wrlock(&mRWLock); +} +inline status_t RWLock::tryWriteLock() { + return -pthread_rwlock_trywrlock(&mRWLock); +} +inline void RWLock::unlock() { + pthread_rwlock_unlock(&mRWLock); +} + +#endif // HAVE_PTHREADS + +/*****************************************************************************/ + /* * Condition variable class. The implementation is system-dependent. * diff --git a/libs/audioflinger/AudioDumpInterface.cpp b/libs/audioflinger/AudioDumpInterface.cpp index a018b4c..6c11114 100644 --- a/libs/audioflinger/AudioDumpInterface.cpp +++ b/libs/audioflinger/AudioDumpInterface.cpp @@ -32,7 +32,7 @@ namespace android { // ---------------------------------------------------------------------------- AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) - : mFirstHwOutput(true), mPolicyCommands(String8("")), mFileName(String8("")) + : mPolicyCommands(String8("")), mFileName(String8("")) { if(hw == 0) { LOGE("Dump construct hw = 0"); @@ -47,6 +47,11 @@ AudioDumpInterface::~AudioDumpInterface() for (size_t i = 0; i < mOutputs.size(); i++) { closeOutputStream((AudioStreamOut *)mOutputs[i]); } + + for (size_t i = 0; i < mInputs.size(); i++) { + closeInputStream((AudioStreamIn *)mInputs[i]); + } + if(mFinalInterface) delete mFinalInterface; } @@ -60,31 +65,32 @@ AudioStreamOut* AudioDumpInterface::openOutputStream( uint32_t lRate = 44100; - if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices) || mFirstHwOutput) { - outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status); - if (outFinal != 0) { - lFormat = outFinal->format(); - lChannels = outFinal->channels(); - lRate = outFinal->sampleRate(); - if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) { - mFirstHwOutput = false; - } - } + outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status); + if (outFinal != 0) { + lFormat = outFinal->format(); + lChannels = outFinal->channels(); + lRate = outFinal->sampleRate(); } else { - if (format != 0 && *format != 0) { - lFormat = *format; - } else { - lFormat = AudioSystem::PCM_16_BIT; + if (format != 0) { + if (*format != 0) { + lFormat = *format; + } else { + *format = lFormat; + } } - if (channels != 0 && *channels != 0) { - lChannels = *channels; - } else { - lChannels = AudioSystem::CHANNEL_OUT_STEREO; + if (channels != 0) { + if (*channels != 0) { + lChannels = *channels; + } else { + *channels = lChannels; + } } - if (sampleRate != 0 && *sampleRate != 0) { - lRate = *sampleRate; - } else { - lRate = 44100; + if (sampleRate != 0) { + if (*sampleRate != 0) { + lRate = *sampleRate; + } else { + *sampleRate = lRate; + } } if (status) *status = NO_ERROR; } @@ -111,7 +117,6 @@ void AudioDumpInterface::closeOutputStream(AudioStreamOut* out) dumpOut->standby(); if (dumpOut->finalStream() != NULL) { mFinalInterface->closeOutputStream(dumpOut->finalStream()); - mFirstHwOutput = true; } mOutputs.remove(dumpOut); @@ -126,18 +131,33 @@ AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO; uint32_t lRate = 8000; - - if (mInputs.size() == 0) { - inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); - if (inFinal == 0) return 0; - + inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); + if (inFinal != 0) { lFormat = inFinal->format(); lChannels = inFinal->channels(); lRate = inFinal->sampleRate(); } else { - if (format != 0 && *format != 0) lFormat = *format; - if (channels != 0 && *channels != 0) lChannels = *channels; - if (sampleRate != 0 && *sampleRate != 0) lRate = *sampleRate; + if (format != 0) { + if (*format != 0) { + lFormat = *format; + } else { + *format = lFormat; + } + } + if (channels != 0) { + if (*channels != 0) { + lChannels = *channels; + } else { + *channels = lChannels; + } + } + if (sampleRate != 0) { + if (*sampleRate != 0) { + lRate = *sampleRate; + } else { + *sampleRate = lRate; + } + } if (status) *status = NO_ERROR; } LOGV("openInputStream(), inFinal %p", inFinal); @@ -223,6 +243,15 @@ String8 AudioDumpInterface::getParameters(const String8& keys) return keyValuePairs; } +status_t AudioDumpInterface::setMode(int mode) +{ + return mFinalInterface->setMode(mode); +} + +size_t AudioDumpInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mFinalInterface->getInputBufferSize(sampleRate, format, channelCount); +} // ---------------------------------------------------------------------------- @@ -235,7 +264,7 @@ AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface, uint32_t sampleRate) : mInterface(interface), mId(id), mSampleRate(sampleRate), mFormat(format), mChannels(channels), mLatency(0), mDevice(devices), - mBufferSize(1024), mFinalStream(finalStream), mOutFile(0), mFileCount(0) + mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0) { LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); } @@ -254,26 +283,26 @@ ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) if (mFinalStream) { ret = mFinalStream->write(buffer, bytes); } else { - usleep((bytes * 1000000) / frameSize() / sampleRate()); + usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000); ret = bytes; } - if(!mOutFile) { + if(!mFile) { if (mInterface->fileName() != "") { char name[255]; - sprintf(name, "%s_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount); - mOutFile = fopen(name, "wb"); - LOGV("Opening dump file %s, fh %p", name, mOutFile); + sprintf(name, "%s_out_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount); + mFile = fopen(name, "wb"); + LOGV("Opening dump file %s, fh %p", name, mFile); } } - if (mOutFile) { - fwrite(buffer, bytes, 1, mOutFile); + if (mFile) { + fwrite(buffer, bytes, 1, mFile); } return ret; } status_t AudioStreamOutDump::standby() { - LOGV("AudioStreamOutDump standby(), mOutFile %p, mFinalStream %p", mOutFile, mFinalStream); + LOGV("AudioStreamOutDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream); Close(); if (mFinalStream != 0 ) return mFinalStream->standby(); @@ -330,7 +359,7 @@ status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs) } if (param.getInt(String8("format"), valueInt) == NO_ERROR) { - if (mOutFile == 0) { + if (mFile == 0) { mFormat = valueInt; } else { status = INVALID_OPERATION; @@ -345,7 +374,7 @@ status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs) } if (param.getInt(String8("sampling_rate"), valueInt) == NO_ERROR) { if (valueInt > 0 && valueInt <= 48000) { - if (mOutFile == 0) { + if (mFile == 0) { mSampleRate = valueInt; } else { status = INVALID_OPERATION; @@ -373,9 +402,9 @@ status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args) void AudioStreamOutDump::Close() { - if(mOutFile) { - fclose(mOutFile); - mOutFile = 0; + if(mFile) { + fclose(mFile); + mFile = 0; } } @@ -396,7 +425,7 @@ AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface, uint32_t sampleRate) : mInterface(interface), mId(id), mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices), - mBufferSize(1024), mFinalStream(finalStream), mInFile(0) + mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0) { LOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); } @@ -409,55 +438,68 @@ AudioStreamInDump::~AudioStreamInDump() ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes) { - if (mFinalStream) { - return mFinalStream->read(buffer, bytes); - } - - usleep((bytes * 1000000) / frameSize() / sampleRate()); + ssize_t ret; - if(!mInFile) { - char name[255]; - strcpy(name, "/sdcard/music/sine440"); - if (channels() == AudioSystem::CHANNEL_IN_MONO) { - strcat(name, "_mo"); - } else { - strcat(name, "_st"); + if (mFinalStream) { + ret = mFinalStream->read(buffer, bytes); + if(!mFile) { + if (mInterface->fileName() != "") { + char name[255]; + sprintf(name, "%s_in_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount); + mFile = fopen(name, "wb"); + LOGV("Opening input dump file %s, fh %p", name, mFile); + } } - if (format() == AudioSystem::PCM_16_BIT) { - strcat(name, "_16b"); - } else { - strcat(name, "_8b"); + if (mFile) { + fwrite(buffer, bytes, 1, mFile); } - if (sampleRate() < 16000) { - strcat(name, "_8k"); - } else if (sampleRate() < 32000) { - strcat(name, "_22k"); - } else if (sampleRate() < 48000) { - strcat(name, "_44k"); - } else { - strcat(name, "_48k"); - } - strcat(name, ".wav"); - mInFile = fopen(name, "rb"); - LOGV("Opening dump file %s, fh %p", name, mInFile); - if (mInFile) { - fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); + } else { + usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000); + ret = bytes; + if(!mFile) { + char name[255]; + strcpy(name, "/sdcard/music/sine440"); + if (channels() == AudioSystem::CHANNEL_IN_MONO) { + strcat(name, "_mo"); + } else { + strcat(name, "_st"); + } + if (format() == AudioSystem::PCM_16_BIT) { + strcat(name, "_16b"); + } else { + strcat(name, "_8b"); + } + if (sampleRate() < 16000) { + strcat(name, "_8k"); + } else if (sampleRate() < 32000) { + strcat(name, "_22k"); + } else if (sampleRate() < 48000) { + strcat(name, "_44k"); + } else { + strcat(name, "_48k"); + } + strcat(name, ".wav"); + mFile = fopen(name, "rb"); + LOGV("Opening input read file %s, fh %p", name, mFile); + if (mFile) { + fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); + } } - - } - if (mInFile) { - ssize_t bytesRead = fread(buffer, bytes, 1, mInFile); - if (bytesRead != bytes) { - fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); - fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mInFile); + if (mFile) { + ssize_t bytesRead = fread(buffer, bytes, 1, mFile); + if (bytesRead >=0 && bytesRead < bytes) { + fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); + fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mFile); + } } } - return bytes; + + return ret; } status_t AudioStreamInDump::standby() { - LOGV("AudioStreamInDump standby(), mInFile %p, mFinalStream %p", mInFile, mFinalStream); + LOGV("AudioStreamInDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream); Close(); if (mFinalStream != 0 ) return mFinalStream->standby(); @@ -523,9 +565,9 @@ status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args) void AudioStreamInDump::Close() { - if(mInFile) { - fclose(mInFile); - mInFile = 0; + if(mFile) { + fclose(mFile); + mFile = 0; } } }; // namespace android diff --git a/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h index 4c62b3e..814ce5f 100644 --- a/libs/audioflinger/AudioDumpInterface.h +++ b/libs/audioflinger/AudioDumpInterface.h @@ -69,7 +69,7 @@ private: uint32_t mDevice; // current device this output is routed to size_t mBufferSize; AudioStreamOut *mFinalStream; - FILE *mOutFile; // output file + FILE *mFile; // output file int mFileCount; }; @@ -109,7 +109,8 @@ private: uint32_t mDevice; // current device this output is routed to size_t mBufferSize; AudioStreamIn *mFinalStream; - FILE *mInFile; // output file + FILE *mFile; // output file + int mFileCount; }; class AudioDumpInterface : public AudioHardwareBase @@ -134,6 +135,8 @@ public: virtual status_t setMasterVolume(float volume) {return mFinalInterface->setMasterVolume(volume);} + virtual status_t setMode(int mode); + // mic mute virtual status_t setMicMute(bool state) {return mFinalInterface->setMicMute(state);} @@ -143,6 +146,8 @@ public: virtual status_t setParameters(const String8& keyValuePairs); virtual String8 getParameters(const String8& keys); + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + virtual AudioStreamIn* openInputStream(uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics); virtual void closeInputStream(AudioStreamIn* in); @@ -153,8 +158,7 @@ public: protected: AudioHardwareInterface *mFinalInterface; - SortedVector<AudioStreamOutDump *> mOutputs; - bool mFirstHwOutput; + SortedVector<AudioStreamOutDump *> mOutputs; SortedVector<AudioStreamInDump *> mInputs; Mutex mLock; String8 mPolicyCommands; diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index 2414e8d..3b38d83 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -142,6 +142,7 @@ AudioFlinger::AudioFlinger() } #ifdef LVMX LifeVibes::init(); + mLifeVibesClientPid = -1; #endif } @@ -596,8 +597,10 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) int musicEnabled = -1; if (NO_ERROR == param.get(key, value)) { if (value == LifevibesEnable) { + mLifeVibesClientPid = IPCThreadState::self()->getCallingPid(); musicEnabled = 1; } else if (value == LifevibesDisable) { + mLifeVibesClientPid = -1; musicEnabled = 0; } } @@ -609,7 +612,7 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) mHardwareStatus = AUDIO_SET_PARAMETER; result = mAudioHardware->setParameters(keyValuePairs); #ifdef LVMX - if ((NO_ERROR == result) && (musicEnabled != -1)) { + if (musicEnabled != -1) { LifeVibes::enableMusic((bool) musicEnabled); } #endif @@ -713,51 +716,57 @@ status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrame void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) { - LOGV("registerClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid()); Mutex::Autolock _l(mLock); - sp<IBinder> binder = client->asBinder(); - if (mNotificationClients.indexOf(binder) < 0) { - LOGV("Adding notification client %p", binder.get()); - binder->linkToDeath(this); - mNotificationClients.add(binder); - } + int pid = IPCThreadState::self()->getCallingPid(); + if (mNotificationClients.indexOfKey(pid) < 0) { + sp<NotificationClient> notificationClient = new NotificationClient(this, + client, + pid); + LOGV("registerClient() client %p, pid %d", notificationClient.get(), pid); - // the config change is always sent from playback or record threads to avoid deadlock - // with AudioSystem::gLock - for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED); - } + mNotificationClients.add(pid, notificationClient); + + sp<IBinder> binder = client->asBinder(); + binder->linkToDeath(notificationClient); + + // the config change is always sent from playback or record threads to avoid deadlock + // with AudioSystem::gLock + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED); + } - for (size_t i = 0; i < mRecordThreads.size(); i++) { - mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED); + for (size_t i = 0; i < mRecordThreads.size(); i++) { + mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED); + } } } -void AudioFlinger::binderDied(const wp<IBinder>& who) { - - LOGV("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid()); +void AudioFlinger::removeNotificationClient(pid_t pid) +{ Mutex::Autolock _l(mLock); - IBinder *binder = who.unsafe_get(); - - if (binder != NULL) { - int index = mNotificationClients.indexOf(binder); - if (index >= 0) { - LOGV("Removing notification client %p", binder); - mNotificationClients.removeAt(index); + int index = mNotificationClients.indexOfKey(pid); + if (index >= 0) { + sp <NotificationClient> client = mNotificationClients.valueFor(pid); + LOGV("removeNotificationClient() %p, pid %d", client.get(), pid); +#ifdef LVMX + if (pid == mLifeVibesClientPid) { + LOGV("Disabling lifevibes"); + LifeVibes::enableMusic(false); + mLifeVibesClientPid = -1; } +#endif + mNotificationClients.removeItem(pid); } } // audioConfigChanged_l() must be called with AudioFlinger::mLock held -void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2) { +void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2) +{ size_t size = mNotificationClients.size(); for (size_t i = 0; i < size; i++) { - sp<IBinder> binder = mNotificationClients.itemAt(i); - LOGV("audioConfigChanged_l() Notifying change to client %p", binder.get()); - sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); - client->ioConfigChanged(event, ioHandle, param2); + mNotificationClients.valueAt(i)->client()->ioConfigChanged(event, ioHandle, param2); } } @@ -768,12 +777,13 @@ void AudioFlinger::removeClient_l(pid_t pid) mClients.removeItem(pid); } + // ---------------------------------------------------------------------------- AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id) : Thread(false), mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0), - mFormat(0), mFrameSize(1), mStandby(false), mId(id), mExiting(false) + mFrameSize(1), mFormat(0), mStandby(false), mId(id), mExiting(false) { } @@ -806,7 +816,7 @@ uint32_t AudioFlinger::ThreadBase::sampleRate() const int AudioFlinger::ThreadBase::channelCount() const { - return mChannelCount; + return (int)mChannelCount; } int AudioFlinger::ThreadBase::format() const @@ -863,11 +873,12 @@ void AudioFlinger::ThreadBase::processConfigEvents() LOGV("processConfigEvents() remaining events %d", mConfigEvents.size()); ConfigEvent *configEvent = mConfigEvents[0]; mConfigEvents.removeAt(0); - // release mLock because audioConfigChanged() will lock AudioFlinger mLock - // before calling Audioflinger::audioConfigChanged_l() thus creating - // potential cross deadlock between AudioFlinger::mLock and mLock + // release mLock before locking AudioFlinger mLock: lock order is always + // AudioFlinger then ThreadBase to avoid cross deadlock mLock.unlock(); - audioConfigChanged(configEvent->mEvent, configEvent->mParam); + mAudioFlinger->mLock.lock(); + audioConfigChanged_l(configEvent->mEvent, configEvent->mParam); + mAudioFlinger->mLock.unlock(); delete configEvent; mLock.lock(); } @@ -943,8 +954,6 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream); mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream); } - // notify client processes that a new input has been opened - sendConfigEvent(AudioSystem::OUTPUT_OPENED); } AudioFlinger::PlaybackThread::~PlaybackThread() @@ -1054,7 +1063,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra status_t lStatus; if (mType == DIRECT) { - if (sampleRate != mSampleRate || format != mFormat || channelCount != mChannelCount) { + if (sampleRate != mSampleRate || format != mFormat || channelCount != (int)mChannelCount) { LOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelCount %d for output %p", sampleRate, format, channelCount, mOutput); lStatus = BAD_VALUE; @@ -1224,16 +1233,17 @@ String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys) return mOutput->getParameters(keys); } -void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) { +// destroyTrack_l() must be called with AudioFlinger::mLock held +void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) { AudioSystem::OutputDescriptor desc; void *param2 = 0; - LOGV("PlaybackThread::audioConfigChanged, thread %p, event %d, param %d", this, event, param); + LOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, param); switch (event) { case AudioSystem::OUTPUT_OPENED: case AudioSystem::OUTPUT_CONFIG_CHANGED: - desc.channels = mChannelCount; + desc.channels = mChannels; desc.samplingRate = mSampleRate; desc.format = mFormat; desc.frameCount = mFrameCount; @@ -1247,17 +1257,16 @@ void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) { default: break; } - Mutex::Autolock _l(mAudioFlinger->mLock); mAudioFlinger->audioConfigChanged_l(event, mId, param2); } void AudioFlinger::PlaybackThread::readOutputParameters() { mSampleRate = mOutput->sampleRate(); - mChannelCount = AudioSystem::popCount(mOutput->channels()); - + mChannels = mOutput->channels(); + mChannelCount = (uint16_t)AudioSystem::popCount(mChannels); mFormat = mOutput->format(); - mFrameSize = mOutput->frameSize(); + mFrameSize = (uint16_t)mOutput->frameSize(); mFrameCount = mOutput->bufferSize() / mFrameSize; // FIXME - Current mixer implementation only supports stereo output: Always @@ -1604,66 +1613,22 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track return mixerStatus; } -void AudioFlinger::MixerThread::getTracks( - SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks, - int streamType) +void AudioFlinger::MixerThread::invalidateTracks(int streamType) { - LOGV ("MixerThread::getTracks() mixer %p, mTracks.size %d, mActiveTracks.size %d", this, mTracks.size(), mActiveTracks.size()); + LOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d", this, streamType, mTracks.size()); Mutex::Autolock _l(mLock); size_t size = mTracks.size(); for (size_t i = 0; i < size; i++) { sp<Track> t = mTracks[i]; if (t->type() == streamType) { - tracks.add(t); - int j = mActiveTracks.indexOf(t); - if (j >= 0) { - t = mActiveTracks[j].promote(); - if (t != NULL) { - activeTracks.add(t); + t->mCblk->lock.lock(); + t->mCblk->flags |= CBLK_INVALID_ON; + t->mCblk->cv.signal(); + t->mCblk->lock.unlock(); } } } - } - - size = activeTracks.size(); - for (size_t i = 0; i < size; i++) { - mActiveTracks.remove(activeTracks[i]); - } - - size = tracks.size(); - for (size_t i = 0; i < size; i++) { - sp<Track> t = tracks[i]; - mTracks.remove(t); - deleteTrackName_l(t->name()); - } -} - -void AudioFlinger::MixerThread::putTracks( - SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks) -{ - LOGV ("MixerThread::putTracks() mixer %p, tracks.size %d, activeTracks.size %d", this, tracks.size(), activeTracks.size()); - Mutex::Autolock _l(mLock); - size_t size = tracks.size(); - for (size_t i = 0; i < size ; i++) { - sp<Track> t = tracks[i]; - int name = getTrackName_l(); - - if (name < 0) return; - - t->mName = name; - t->mThread = this; - mTracks.add(t); - int j = activeTracks.indexOf(t); - if (j >= 0) { - mActiveTracks.add(t); - // force buffer refilling and no ramp volume when the track is mixed for the first time - t->mFillingUpStatus = Track::FS_FILLING; - } - } -} // getTrackName_l() must be called with ThreadBase::mLock held int AudioFlinger::MixerThread::getTrackName_l() @@ -2332,13 +2297,13 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( // clear all buffers mCblk->frameCount = frameCount; mCblk->sampleRate = sampleRate; - mCblk->channels = (uint8_t)channelCount; + mCblk->channelCount = (uint8_t)channelCount; if (sharedBuffer == 0) { mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); // Force underrun condition to avoid false underrun callback until first data is // written to buffer - mCblk->flowControlFlag = 1; + mCblk->flags = CBLK_UNDERRUN_ON; } else { mBuffer = sharedBuffer->pointer(); } @@ -2356,12 +2321,12 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( // clear all buffers mCblk->frameCount = frameCount; mCblk->sampleRate = sampleRate; - mCblk->channels = (uint8_t)channelCount; + mCblk->channelCount = (uint8_t)channelCount; mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); // Force underrun condition to avoid false underrun callback until first data is // written to buffer - mCblk->flowControlFlag = 1; + mCblk->flags = CBLK_UNDERRUN_ON; mBufferEnd = (uint8_t *)mBuffer + bufferSize; } } @@ -2423,7 +2388,7 @@ int AudioFlinger::ThreadBase::TrackBase::sampleRate() const { } int AudioFlinger::ThreadBase::TrackBase::channelCount() const { - return (int)mCblk->channels; + return (int)mCblk->channelCount; } void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { @@ -2435,9 +2400,9 @@ void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t f if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd || ((unsigned long)bufferStart & (unsigned long)(cblk->frameSize - 1))) { LOGE("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \ - server %d, serverBase %d, user %d, userBase %d, channels %d", + server %d, serverBase %d, user %d, userBase %d, channelCount %d", bufferStart, bufferEnd, mBuffer, mBufferEnd, - cblk->server, cblk->serverBase, cblk->user, cblk->userBase, cblk->channels); + cblk->server, cblk->serverBase, cblk->user, cblk->userBase, cblk->channelCount); return 0; } @@ -2522,7 +2487,7 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) (mClient == NULL) ? getpid() : mClient->pid(), mStreamType, mFormat, - mCblk->channels, + mCblk->channelCount, mFrameCount, mState, mMute, @@ -2579,9 +2544,9 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const { if (mFillingUpStatus != FS_FILLING) return true; if (mCblk->framesReady() >= mCblk->frameCount || - mCblk->forceReady) { + (mCblk->flags & CBLK_FORCEREADY_MSK)) { mFillingUpStatus = FS_FILLED; - mCblk->forceReady = 0; + mCblk->flags &= ~CBLK_FORCEREADY_MSK; return true; } return false; @@ -2696,8 +2661,8 @@ void AudioFlinger::PlaybackThread::Track::reset() TrackBase::reset(); // Force underrun condition to avoid false underrun callback until first data is // written to buffer - mCblk->flowControlFlag = 1; - mCblk->forceReady = 0; + mCblk->flags |= CBLK_UNDERRUN_ON; + mCblk->flags &= ~CBLK_FORCEREADY_MSK; mFillingUpStatus = FS_FILLING; mResetDone = true; } @@ -2808,7 +2773,7 @@ void AudioFlinger::RecordThread::RecordTrack::stop() TrackBase::reset(); // Force overerrun condition to avoid false overrun callback until first data is // read from buffer - mCblk->flowControlFlag = 1; + mCblk->flags |= CBLK_UNDERRUN_ON; } } @@ -2817,7 +2782,7 @@ void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size) snprintf(buffer, size, " %05d %03u %03u %04u %01d %05u %08x %08x\n", (mClient == NULL) ? getpid() : mClient->pid(), mFormat, - mCblk->channels, + mCblk->channelCount, mFrameCount, mState, mCblk->sampleRate, @@ -2841,13 +2806,13 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get(); if (mCblk != NULL) { - mCblk->out = 1; + mCblk->flags |= CBLK_DIRECTION_OUT; mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); mCblk->volume[0] = mCblk->volume[1] = 0x1000; mOutBuffer.frameCount = 0; playbackThread->mTracks.add(this); - LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p", - mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd); + LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channelCount %d mBufferEnd %p", + mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channelCount, mBufferEnd); } else { LOGW("Error creating output track on thread %p", playbackThread); } @@ -2882,7 +2847,7 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr { Buffer *pInBuffer; Buffer inBuffer; - uint32_t channels = mCblk->channels; + uint32_t channelCount = mCblk->channelCount; bool outputBufferFull = false; inBuffer.frameCount = frames; inBuffer.i16 = data; @@ -2898,10 +2863,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr if (mBufferQueue.size() < kMaxOverFlowBuffers) { uint32_t startFrames = (mCblk->frameCount - frames); pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[startFrames * channels]; + pInBuffer->mBuffer = new int16_t[startFrames * channelCount]; pInBuffer->frameCount = startFrames; pInBuffer->i16 = pInBuffer->mBuffer; - memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t)); + memset(pInBuffer->raw, 0, startFrames * channelCount * sizeof(int16_t)); mBufferQueue.add(pInBuffer); } else { LOGW ("OutputTrack::write() %p no more buffers in queue", this); @@ -2939,12 +2904,12 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr } uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount; - memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t)); + memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t)); mCblk->stepUser(outFrames); pInBuffer->frameCount -= outFrames; - pInBuffer->i16 += outFrames * channels; + pInBuffer->i16 += outFrames * channelCount; mOutBuffer.frameCount -= outFrames; - mOutBuffer.i16 += outFrames * channels; + mOutBuffer.i16 += outFrames * channelCount; if (pInBuffer->frameCount == 0) { if (mBufferQueue.size()) { @@ -2964,10 +2929,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr if (thread != 0 && !thread->standby()) { if (mBufferQueue.size() < kMaxOverFlowBuffers) { pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels]; + pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channelCount]; pInBuffer->frameCount = inBuffer.frameCount; pInBuffer->i16 = pInBuffer->mBuffer; - memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t)); + memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channelCount * sizeof(int16_t)); mBufferQueue.add(pInBuffer); LOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size()); } else { @@ -2983,10 +2948,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr if (mCblk->user < mCblk->frameCount) { frames = mCblk->frameCount - mCblk->user; pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[frames * channels]; + pInBuffer->mBuffer = new int16_t[frames * channelCount]; pInBuffer->frameCount = frames; pInBuffer->i16 = pInBuffer->mBuffer; - memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t)); + memset(pInBuffer->raw, 0, frames * channelCount * sizeof(int16_t)); mBufferQueue.add(pInBuffer); } else if (mActive) { stop(); @@ -3086,6 +3051,28 @@ const sp<MemoryDealer>& AudioFlinger::Client::heap() const // ---------------------------------------------------------------------------- +AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger, + const sp<IAudioFlingerClient>& client, + pid_t pid) + : mAudioFlinger(audioFlinger), mPid(pid), mClient(client) +{ +} + +AudioFlinger::NotificationClient::~NotificationClient() +{ + mClient.clear(); +} + +void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who) +{ + sp<NotificationClient> keep(this); + { + mAudioFlinger->removeNotificationClient(mPid); + } +} + +// ---------------------------------------------------------------------------- + AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track) : BnAudioTrack(), mTrack(track) @@ -3242,7 +3229,6 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, A mReqChannelCount = AudioSystem::popCount(channels); mReqSampleRate = sampleRate; readInputParameters(); - sendConfigEvent(AudioSystem::INPUT_OPENED); } @@ -3339,7 +3325,7 @@ bool AudioFlinger::RecordThread::threadLoop() framesIn = framesOut; mRsmpInIndex += framesIn; framesOut -= framesIn; - if (mChannelCount == mReqChannelCount || + if ((int)mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT) { memcpy(dst, src, framesIn * mFrameSize); } else { @@ -3360,7 +3346,7 @@ bool AudioFlinger::RecordThread::threadLoop() } if (framesOut && mFrameCount == mRsmpInIndex) { if (framesOut == mFrameCount && - (mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) { + ((int)mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) { mBytesRead = mInput->read(buffer.raw, mInputBytes); framesOut = 0; } else { @@ -3657,14 +3643,14 @@ String8 AudioFlinger::RecordThread::getParameters(const String8& keys) return mInput->getParameters(keys); } -void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) { +void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) { AudioSystem::OutputDescriptor desc; void *param2 = 0; switch (event) { case AudioSystem::INPUT_OPENED: case AudioSystem::INPUT_CONFIG_CHANGED: - desc.channels = mChannelCount; + desc.channels = mChannels; desc.samplingRate = mSampleRate; desc.format = mFormat; desc.frameCount = mFrameCount; @@ -3676,7 +3662,6 @@ void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) { default: break; } - Mutex::Autolock _l(mAudioFlinger->mLock); mAudioFlinger->audioConfigChanged_l(event, mId, param2); } @@ -3688,9 +3673,10 @@ void AudioFlinger::RecordThread::readInputParameters() mResampler = 0; mSampleRate = mInput->sampleRate(); - mChannelCount = AudioSystem::popCount(mInput->channels()); + mChannels = mInput->channels(); + mChannelCount = (uint16_t)AudioSystem::popCount(mChannels); mFormat = mInput->format(); - mFrameSize = mInput->frameSize(); + mFrameSize = (uint16_t)mInput->frameSize(); mInputBytes = mInput->bufferSize(); mFrameCount = mInputBytes / mFrameSize; mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount]; @@ -3795,6 +3781,8 @@ int AudioFlinger::openOutput(uint32_t *pDevices, if (pChannels) *pChannels = channels; if (pLatencyMs) *pLatencyMs = thread->latency(); + // notify client processes of the new output creation + thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); return mNextThreadId; } @@ -3816,6 +3804,8 @@ int AudioFlinger::openDuplicateOutput(int output1, int output2) DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId); thread->addOutputTrack(thread2); mPlaybackThreads.add(mNextThreadId, thread); + // notify client processes of the new output creation + thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); return mNextThreadId; } @@ -3945,6 +3935,8 @@ int AudioFlinger::openInput(uint32_t *pDevices, input->standby(); + // notify client processes of the new input creation + thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED); return mNextThreadId; } @@ -3985,22 +3977,16 @@ status_t AudioFlinger::setStreamOutput(uint32_t stream, int output) } LOGV("setStreamOutput() stream %d to output %d", stream, output); + audioConfigChanged_l(AudioSystem::STREAM_CONFIG_CHANGED, output, &stream); for (size_t i = 0; i < mPlaybackThreads.size(); i++) { PlaybackThread *thread = mPlaybackThreads.valueAt(i).get(); if (thread != dstThread && thread->type() != PlaybackThread::DIRECT) { MixerThread *srcThread = (MixerThread *)thread; - SortedVector < sp<MixerThread::Track> > tracks; - SortedVector < wp<MixerThread::Track> > activeTracks; - srcThread->getTracks(tracks, activeTracks, stream); - if (tracks.size()) { - dstThread->putTracks(tracks, activeTracks); + srcThread->invalidateTracks(stream); } } - } - - dstThread->sendConfigEvent(AudioSystem::STREAM_CONFIG_CHANGED, stream); return NO_ERROR; } diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index 739ec33..f35f38b 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -57,7 +57,7 @@ class AudioResampler; static const nsecs_t kStandbyTimeInNsecs = seconds(3); -class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient +class AudioFlinger : public BnAudioFlinger { public: static void instantiate(); @@ -139,9 +139,6 @@ public: virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output); - // IBinder::DeathRecipient - virtual void binderDied(const wp<IBinder>& who); - enum hardware_call_state { AUDIO_HW_IDLE = 0, AUDIO_HW_INIT, @@ -205,6 +202,27 @@ private: pid_t mPid; }; + // --- Notification Client --- + class NotificationClient : public IBinder::DeathRecipient { + public: + NotificationClient(const sp<AudioFlinger>& audioFlinger, + const sp<IAudioFlingerClient>& client, + pid_t pid); + virtual ~NotificationClient(); + + sp<IAudioFlingerClient> client() { return mClient; } + + // IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + + private: + NotificationClient(const NotificationClient&); + NotificationClient& operator = (const NotificationClient&); + + sp<AudioFlinger> mAudioFlinger; + pid_t mPid; + sp<IAudioFlingerClient> mClient; + }; class TrackHandle; class RecordHandle; @@ -324,7 +342,7 @@ private: virtual bool checkForNewParameters_l() = 0; virtual status_t setParameters(const String8& keyValuePairs); virtual String8 getParameters(const String8& keys) = 0; - virtual void audioConfigChanged(int event, int param = 0) = 0; + virtual void audioConfigChanged_l(int event, int param = 0) = 0; void sendConfigEvent(int event, int param = 0); void sendConfigEvent_l(int event, int param = 0); void processConfigEvents(); @@ -348,9 +366,10 @@ private: sp<AudioFlinger> mAudioFlinger; uint32_t mSampleRate; size_t mFrameCount; - int mChannelCount; + uint32_t mChannels; + uint16_t mChannelCount; + uint16_t mFrameSize; int mFormat; - uint32_t mFrameSize; Condition mParamCond; Vector<String8> mNewParameters; status_t mParamStatus; @@ -528,7 +547,7 @@ private: void restore() { if (mSuspended) mSuspended--; } bool isSuspended() { return (mSuspended != 0); } virtual String8 getParameters(const String8& keys); - virtual void audioConfigChanged(int event, int param = 0); + virtual void audioConfigChanged_l(int event, int param = 0); virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames); struct stream_type_t { @@ -594,11 +613,7 @@ private: // Thread virtuals virtual bool threadLoop(); - void getTracks(SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks, - int streamType); - void putTracks(SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks); + void invalidateTracks(int streamType); virtual bool checkForNewParameters_l(); virtual status_t dumpInternals(int fd, const Vector<String16>& args); @@ -685,6 +700,7 @@ private: void removeClient_l(pid_t pid); + void removeNotificationClient(pid_t pid); // record thread @@ -744,7 +760,7 @@ private: virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); virtual bool checkForNewParameters_l(); virtual String8 getParameters(const String8& keys); - virtual void audioConfigChanged(int event, int param = 0); + virtual void audioConfigChanged_l(int event, int param = 0); void readInputParameters(); virtual unsigned int getInputFramesLost(); @@ -796,8 +812,11 @@ private: DefaultKeyedVector< int, sp<RecordThread> > mRecordThreads; - SortedVector< sp<IBinder> > mNotificationClients; + DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients; int mNextThreadId; +#ifdef LVMX + int mLifeVibesClientPid; +#endif }; // ---------------------------------------------------------------------------- diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp index c8b3f48..381a958 100644 --- a/libs/audioflinger/AudioPolicyManagerBase.cpp +++ b/libs/audioflinger/AudioPolicyManagerBase.cpp @@ -1249,6 +1249,17 @@ void AudioPolicyManagerBase::closeA2dpOutputs() LOGV("setDeviceConnectionState() closing A2DP and duplicated output!"); if (mDuplicatedOutput != 0) { + AudioOutputDescriptor *dupOutputDesc = mOutputs.valueFor(mDuplicatedOutput); + AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); + // As all active tracks on duplicated output will be deleted, + // and as they were also referenced on hardware output, the reference + // count for their stream type must be adjusted accordingly on + // hardware output. + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + int refCount = dupOutputDesc->mRefCount[i]; + hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); + } + mpClientInterface->closeOutput(mDuplicatedOutput); delete mOutputs.valueFor(mDuplicatedOutput); mOutputs.removeItem(mDuplicatedOutput); @@ -1288,11 +1299,6 @@ void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy, u for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { if (getStrategy((AudioSystem::stream_type)i) == strategy) { mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput); - int refCount = a2dpOutputDesc->mRefCount[i]; - // in the case of duplicated output, the ref count is first incremented - // and then decremented on hardware output tus keeping its value - hwOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount); - a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); } } // do not change newDevice if it was already set before this call by a previous call to @@ -1318,11 +1324,6 @@ void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy, u for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { if (getStrategy((AudioSystem::stream_type)i) == strategy) { mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, a2dpOutput); - int refCount = hwOutputDesc->mRefCount[i]; - // in the case of duplicated output, the ref count is first incremented - // and then decremented on hardware output tus keeping its value - a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount); - hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); } } } diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index 86eb78d..b8a0630 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -13,6 +13,7 @@ LOCAL_SRC_FILES:= \ LayerDim.cpp \ MessageQueue.cpp \ SurfaceFlinger.cpp \ + TextureManager.cpp \ Tokenizer.cpp \ Transform.cpp diff --git a/libs/surfaceflinger/Barrier.h b/libs/surfaceflinger/Barrier.h index e2bcf6a..6f8507e 100644 --- a/libs/surfaceflinger/Barrier.h +++ b/libs/surfaceflinger/Barrier.h @@ -29,10 +29,6 @@ public: inline Barrier() : state(CLOSED) { } inline ~Barrier() { } void open() { - // gcc memory barrier, this makes sure all memory writes - // have been issued by gcc. On an SMP system we'd need a real - // h/w barrier. - asm volatile ("":::"memory"); Mutex::Autolock _l(lock); state = OPENED; cv.broadcast(); diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index ea68352..51de1da 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -73,7 +73,7 @@ void checkEGLErrors(const char* token) DisplayHardware::DisplayHardware( const sp<SurfaceFlinger>& flinger, uint32_t dpy) - : DisplayHardwareBase(flinger, dpy) + : DisplayHardwareBase(flinger, dpy), mFlags(0) { init(dpy); } @@ -125,7 +125,6 @@ void DisplayHardware::init(uint32_t dpy) EGLint numConfigs=0; EGLSurface surface; EGLContext context; - mFlags = CACHED_BUFFERS; // TODO: all the extensions below should be queried through // eglGetProcAddress(). @@ -253,22 +252,10 @@ void DisplayHardware::init(uint32_t dpy) LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); -#if 0 - // for drivers that don't have proper support for flushing cached buffers - // on gralloc unlock, uncomment this block and test for the specific - // renderer substring - if (strstr(gl_renderer, "<some vendor string>")) { - LOGD("Assuming uncached graphics buffers."); - mFlags &= ~CACHED_BUFFERS; - } -#endif 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 df046af..ebd7c42 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -46,12 +46,10 @@ 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 SWAP_RECTANGLE = 0x00080000, - CACHED_BUFFERS = 0x00100000 }; DisplayHardware( diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index ce7e9aa..3fbb4d3 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -47,47 +47,42 @@ 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), + const sp<Client>& client, int32_t i) + : LayerBaseClient(flinger, display, client, i), + lcblk(NULL), mSecure(false), - mNoEGLImageForSwBuffers(false), mNeedsBlending(true), - mNeedsDithering(false) + mNeedsDithering(false), + mTextureManager(mFlags), + mBufferManager(mTextureManager), + mWidth(0), mHeight(0), mFixedSize(false) { // no OpenGL operation is possible here, since we might not be // in the OpenGL thread. - mFrontBufferIndex = lcblk->getFrontBuffer(); + lcblk = new SharedBufferServer( + client->ctrlblk, i, mBufferManager.getDefaultBufferCount(), + getIdentity()); + + mBufferManager.setActiveBufferIndex( lcblk->getFrontBuffer() ); } Layer::~Layer() { - destroy(); + // FIXME: must be called from the main UI thread + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + mBufferManager.destroy(dpy); + // the actual buffers will be destroyed here + delete lcblk; } -void Layer::destroy() +// called with SurfaceFlinger::mStateLock as soon as the layer is entered +// in the purgatory list +void Layer::onRemoved() { - for (size_t i=0 ; i<NUM_BUFFERS ; i++) { - if (mTextures[i].name != -1U) { - glDeleteTextures(1, &mTextures[i].name); - mTextures[i].name = -1U; - } - if (mTextures[i].image != EGL_NO_IMAGE_KHR) { - EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); - eglDestroyImageKHR(dpy, mTextures[i].image); - mTextures[i].image = EGL_NO_IMAGE_KHR; - } - Mutex::Autolock _l(mLock); - mBuffers[i].clear(); - mWidth = mHeight = 0; - } - mSurface.clear(); + // wake up the condition + lcblk->setStatus(NO_INIT); } sp<LayerBaseClient::Surface> Layer::createSurface() const @@ -97,9 +92,17 @@ sp<LayerBaseClient::Surface> Layer::createSurface() const status_t Layer::ditch() { + // NOTE: Called from the main UI thread + // the layer is not on screen anymore. free as much resources as possible mFreezeLock.clear(); - destroy(); + + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + mBufferManager.destroy(dpy); + mSurface.clear(); + + Mutex::Autolock _l(mLock); + mWidth = mHeight = 0; return NO_ERROR; } @@ -131,24 +134,19 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, mHeight = h; mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; - mNoEGLImageForSwBuffers = !(hwFlags & DisplayHardware::CACHED_BUFFERS); // we use the red index int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED); int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); mNeedsDithering = layerRedsize > displayRedSize; - for (size_t i=0 ; i<NUM_BUFFERS ; i++) { - mBuffers[i] = new GraphicBuffer(); - } mSurface = new SurfaceLayer(mFlinger, clientIndex(), this); return NO_ERROR; } void Layer::reloadTexture(const Region& dirty) { - Mutex::Autolock _l(mLock); - sp<GraphicBuffer> buffer(getFrontBufferLocked()); + sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer()); if (buffer == NULL) { // this situation can happen if we ran out of memory for instance. // not much we can do. continue to use whatever texture was bound @@ -156,118 +154,24 @@ void Layer::reloadTexture(const Region& dirty) return; } - const int index = mFrontBufferIndex; - - // create the new texture name if needed - if (UNLIKELY(mTextures[index].name == -1U)) { - mTextures[index].name = createTexture(); - mTextures[index].width = 0; - mTextures[index].height = 0; - } - #ifdef EGL_ANDROID_image_native_buffer if (mFlags & DisplayHardware::DIRECT_TEXTURE) { - if (buffer->usage & GraphicBuffer::USAGE_HW_TEXTURE) { - if (mTextures[index].dirty) { - if (initializeEglImage(buffer, &mTextures[index]) != NO_ERROR) { - // not sure what we can do here... - mFlags &= ~DisplayHardware::DIRECT_TEXTURE; - goto slowpath; - } - } - } else { - if (mHybridBuffer==0 || (mHybridBuffer->width != buffer->width || - mHybridBuffer->height != buffer->height)) { - mHybridBuffer.clear(); - mHybridBuffer = new GraphicBuffer( - buffer->width, buffer->height, buffer->format, - GraphicBuffer::USAGE_SW_WRITE_OFTEN | - GraphicBuffer::USAGE_HW_TEXTURE); - if (initializeEglImage( - mHybridBuffer, &mTextures[0]) != NO_ERROR) { - // not sure what we can do here... - mFlags &= ~DisplayHardware::DIRECT_TEXTURE; - mHybridBuffer.clear(); - goto slowpath; - } - } - - GGLSurface t; - status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); - LOGE_IF(res, "error %d (%s) locking buffer %p", - res, strerror(res), buffer.get()); - if (res == NO_ERROR) { - Texture* const texture(&mTextures[0]); - - glBindTexture(GL_TEXTURE_2D, texture->name); - - sp<GraphicBuffer> buf(mHybridBuffer); - void* vaddr; - res = buf->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &vaddr); - if (res == NO_ERROR) { - int bpp = 0; - switch (t.format) { - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_RGBA_4444: - bpp = 2; - break; - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - bpp = 4; - break; - default: - if (isSupportedYuvFormat(t.format)) { - // just show the Y plane of YUV buffers - bpp = 1; - break; - } - // oops, we don't handle this format! - LOGE("layer %p, texture=%d, using format %d, which is not " - "supported by the GL", this, texture->name, t.format); - } - if (bpp) { - const Rect bounds(dirty.getBounds()); - size_t src_stride = t.stride; - size_t dst_stride = buf->stride; - if (src_stride == dst_stride && - bounds.width() == t.width && - bounds.height() == t.height) - { - memcpy(vaddr, t.data, t.height * t.stride * bpp); - } else { - GLubyte const * src = t.data + - (bounds.left + bounds.top * src_stride) * bpp; - GLubyte * dst = (GLubyte *)vaddr + - (bounds.left + bounds.top * dst_stride) * bpp; - const size_t length = bounds.width() * bpp; - size_t h = bounds.height(); - src_stride *= bpp; - dst_stride *= bpp; - while (h--) { - memcpy(dst, src, length); - dst += dst_stride; - src += src_stride; - } - } - } - buf->unlock(); - } - buffer->unlock(); - } + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) { + // not sure what we can do here... + mFlags &= ~DisplayHardware::DIRECT_TEXTURE; + goto slowpath; } } else #endif { slowpath: - for (size_t i=0 ; i<NUM_BUFFERS ; i++) { - mTextures[i].image = EGL_NO_IMAGE_KHR; - } GGLSurface t; status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); LOGE_IF(res, "error %d (%s) locking buffer %p", res, strerror(res), buffer.get()); if (res == NO_ERROR) { - loadTexture(&mTextures[0], dirty, t); + mBufferManager.loadTexture(dirty, t); buffer->unlock(); } } @@ -275,11 +179,8 @@ slowpath: void Layer::onDraw(const Region& clip) const { - int index = mFrontBufferIndex; - if (mTextures[index].image == EGL_NO_IMAGE_KHR) - index = 0; - GLuint textureName = mTextures[index].name; - if (UNLIKELY(textureName == -1LU)) { + Texture tex(mBufferManager.getActiveTexture()); + if (tex.name == -1LU) { // the texture has not been created yet, this Layer has // in fact never been drawn into. This happens frequently with // SurfaceView because the WindowManager can't know when the client @@ -305,13 +206,53 @@ void Layer::onDraw(const Region& clip) const } return; } - drawWithOpenGL(clip, mTextures[index]); + drawWithOpenGL(clip, tex); +} + +bool Layer::needsFiltering() const +{ + if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { + // NOTE: there is a race here, because mFixedSize is updated in a + // binder transaction. however, it doesn't really matter since it is + // evaluated each time we draw. To be perfectly correct, this flag + // would have to be associated with a buffer. + if (mFixedSize) + return true; + } + return LayerBase::needsFiltering(); +} + + +status_t Layer::setBufferCount(int bufferCount) +{ + // Ensures our client doesn't go away while we're accessing + // the shared area. + sp<Client> ourClient(client.promote()); + if (ourClient == 0) { + // oops, the client is already gone + return DEAD_OBJECT; + } + + // NOTE: lcblk->resize() is protected by an internal lock + status_t err = lcblk->resize(bufferCount); + if (err == NO_ERROR) + mBufferManager.resize(bufferCount); + + return err; } -sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) +sp<GraphicBuffer> Layer::requestBuffer(int index, + uint32_t reqWidth, uint32_t reqHeight, uint32_t reqFormat, + uint32_t usage) { sp<GraphicBuffer> buffer; + if ((reqWidth | reqHeight | reqFormat) < 0) + return buffer; + + if ((!reqWidth && reqHeight) || (reqWidth && !reqHeight)) + return buffer; + // this ensures our client doesn't go away while we're accessing // the shared area. sp<Client> ourClient(client.promote()); @@ -324,7 +265,7 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) * This is called from the client's Surface::dequeue(). This can happen * at any time, especially while we're in the middle of using the * buffer 'index' as our front buffer. - * + * * Make sure the buffer we're resizing is not the front buffer and has been * dequeued. Once this condition is asserted, we are guaranteed that this * buffer cannot become the front buffer under our feet, since we're called @@ -337,31 +278,33 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) return buffer; } - uint32_t w, h; + uint32_t w, h, f; { // scope for the lock Mutex::Autolock _l(mLock); - w = mWidth; - h = mHeight; - buffer = mBuffers[index]; - - // destroy() could have been called before we get here, we log it - // because it's uncommon, and the code below should handle it - LOGW_IF(buffer==0, - "mBuffers[%d] is null (mWidth=%d, mHeight=%d)", - index, w, h); - - mBuffers[index].clear(); + const bool fixedSizeChanged = mFixedSize != (reqWidth && reqHeight); + const bool formatChanged = mReqFormat != reqFormat; + mReqWidth = reqWidth; + mReqHeight = reqHeight; + mReqFormat = reqFormat; + mFixedSize = reqWidth && reqHeight; + w = reqWidth ? reqWidth : mWidth; + h = reqHeight ? reqHeight : mHeight; + f = reqFormat ? reqFormat : mFormat; + buffer = mBufferManager.detachBuffer(index); + if (fixedSizeChanged || formatChanged) { + lcblk->reallocateAllExcept(index); + } } const uint32_t effectiveUsage = getEffectiveUsage(usage); if (buffer!=0 && buffer->getStrongCount() == 1) { - err = buffer->reallocate(w, h, mFormat, effectiveUsage); + err = buffer->reallocate(w, h, f, effectiveUsage); } else { // here we have to reallocate a new buffer because we could have a // client in our process with a reference to it (eg: status bar), // and we can't release the handle under its feet. buffer.clear(); - buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage); + buffer = new GraphicBuffer(w, h, f, effectiveUsage); err = buffer->initCheck(); } @@ -377,15 +320,7 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) if (err == NO_ERROR && buffer->handle != 0) { Mutex::Autolock _l(mLock); - if (mWidth && mHeight) { - // and we have new buffer - mBuffers[index] = buffer; - // texture is now dirty... - mTextures[index].dirty = true; - } else { - // oops we got killed while we were allocating the buffer - buffer.clear(); - } + mBufferManager.attachBuffer(index, buffer); } return buffer; } @@ -411,15 +346,8 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const } else { // it's allowed to modify the usage flags here, but generally // the requested flags should be honored. - if (mNoEGLImageForSwBuffers) { - if (usage & GraphicBuffer::USAGE_HW_MASK) { - // request EGLImage for h/w buffers only - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - } - } else { - // request EGLImage for all buffers - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - } + // request EGLImage for all buffers + usage |= GraphicBuffer::USAGE_HW_TEXTURE; } return usage; } @@ -429,42 +357,46 @@ uint32_t Layer::doTransaction(uint32_t flags) const Layer::State& front(drawingState()); const Layer::State& temp(currentState()); - if ((front.requested_w != temp.requested_w) || - (front.requested_h != temp.requested_h)) { + const bool sizeChanged = (front.requested_w != temp.requested_w) || + (front.requested_h != temp.requested_h); + + if (sizeChanged) { // the size changed, we need to ask our client to request a new buffer LOGD_IF(DEBUG_RESIZE, - "resize (layer=%p), requested (%dx%d), " - "drawing (%d,%d), (%dx%d), (%dx%d)", - this, - int(temp.requested_w), int(temp.requested_h), - int(front.requested_w), int(front.requested_h), - int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()), - int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight())); - - // we're being resized and there is a freeze display request, - // acquire a freeze lock, so that the screen stays put - // until we've redrawn at the new size; this is to avoid - // glitches upon orientation changes. - if (mFlinger->hasFreezeRequest()) { - // if the surface is hidden, don't try to acquire the - // freeze lock, since hidden surfaces may never redraw - if (!(front.flags & ISurfaceComposer::eLayerHidden)) { - mFreezeLock = mFlinger->getFreezeLock(); + "resize (layer=%p), requested (%dx%d), drawing (%d,%d)", + this, + int(temp.requested_w), int(temp.requested_h), + int(front.requested_w), int(front.requested_h)); + + if (!isFixedSize()) { + // we're being resized and there is a freeze display request, + // acquire a freeze lock, so that the screen stays put + // until we've redrawn at the new size; this is to avoid + // glitches upon orientation changes. + if (mFlinger->hasFreezeRequest()) { + // if the surface is hidden, don't try to acquire the + // freeze lock, since hidden surfaces may never redraw + if (!(front.flags & ISurfaceComposer::eLayerHidden)) { + mFreezeLock = mFlinger->getFreezeLock(); + } } - } - // this will make sure LayerBase::doTransaction doesn't update - // the drawing state's size - Layer::State& editDraw(mDrawingState); - editDraw.requested_w = temp.requested_w; - editDraw.requested_h = temp.requested_h; + // this will make sure LayerBase::doTransaction doesn't update + // the drawing state's size + Layer::State& editDraw(mDrawingState); + editDraw.requested_w = temp.requested_w; + editDraw.requested_h = temp.requested_h; - // record the new size, form this point on, when the client request a - // buffer, it'll get the new size. - setDrawingSize(temp.requested_w, temp.requested_h); + // record the new size, form this point on, when the client request + // a buffer, it'll get the new size. + setBufferSize(temp.requested_w, temp.requested_h); - // all buffers need reallocation - lcblk->reallocate(); + // all buffers need reallocation + lcblk->reallocateAll(); + } else { + // record the new size + setBufferSize(temp.requested_w, temp.requested_h); + } } if (temp.sequence != front.sequence) { @@ -478,12 +410,17 @@ uint32_t Layer::doTransaction(uint32_t flags) return LayerBase::doTransaction(flags); } -void Layer::setDrawingSize(uint32_t w, uint32_t h) { +void Layer::setBufferSize(uint32_t w, uint32_t h) { Mutex::Autolock _l(mLock); mWidth = w; mHeight = h; } +bool Layer::isFixedSize() const { + Mutex::Autolock _l(mLock); + return mFixedSize; +} + // ---------------------------------------------------------------------------- // pageflip handling... // ---------------------------------------------------------------------------- @@ -491,22 +428,25 @@ void Layer::setDrawingSize(uint32_t w, uint32_t h) { void Layer::lockPageFlip(bool& recomputeVisibleRegions) { ssize_t buf = lcblk->retireAndLock(); - if (buf < NO_ERROR) { - //LOGW("nothing to retire (%s)", strerror(-buf)); - // NOTE: here the buffer is locked because we will used + if (buf == NOT_ENOUGH_DATA) { + // NOTE: This is not an error, it simply means there is nothing to + // retire. The buffer is locked because we will use it // for composition later in the loop return; } - // ouch, this really should never happen - if (uint32_t(buf)>=NUM_BUFFERS) { + if (buf < NO_ERROR) { LOGE("retireAndLock() buffer index (%d) out of range", buf); mPostedDirtyRegion.clear(); return; } // we retired a buffer, which becomes the new front buffer - mFrontBufferIndex = buf; + if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) { + LOGE("retireAndLock() buffer index (%d) out of range", buf); + mPostedDirtyRegion.clear(); + return; + } // get the dirty region sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); @@ -559,9 +499,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,17 +531,177 @@ 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(); } } void Layer::finishPageFlip() { - status_t err = lcblk->unlock( mFrontBufferIndex ); - LOGE_IF(err!=NO_ERROR, - "layer %p, buffer=%d wasn't locked!", - this, mFrontBufferIndex); + int buf = mBufferManager.getActiveBufferIndex(); + status_t err = lcblk->unlock( buf ); + LOGE_IF(err!=NO_ERROR, "layer %p, buffer=%d wasn't locked!", this, buf); +} + + +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::BufferManager::BufferManager(TextureManager& tm) + : mNumBuffers(NUM_BUFFERS), mTextureManager(tm), + mActiveBuffer(0), mFailover(false) +{ +} + +Layer::BufferManager::~BufferManager() +{ +} + +status_t Layer::BufferManager::resize(size_t size) +{ + Mutex::Autolock _l(mLock); + mNumBuffers = size; + return NO_ERROR; +} + +// only for debugging +sp<GraphicBuffer> Layer::BufferManager::getBuffer(size_t index) const { + return mBufferData[index].buffer; +} + +status_t Layer::BufferManager::setActiveBufferIndex(size_t index) { + // TODO: need to validate 'index' + mActiveBuffer = index; + return NO_ERROR; +} + +size_t Layer::BufferManager::getActiveBufferIndex() const { + return mActiveBuffer; +} + +Texture Layer::BufferManager::getActiveTexture() const { + Texture res; + if (mFailover) { + res = mFailoverTexture; + } else { + static_cast<Image&>(res) = mBufferData[mActiveBuffer].texture; + } + return res; +} + +sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const { + const size_t activeBuffer = mActiveBuffer; + BufferData const * const buffers = mBufferData; + Mutex::Autolock _l(mLock); + return buffers[activeBuffer].buffer; +} + +sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index) +{ + BufferData* const buffers = mBufferData; + sp<GraphicBuffer> buffer; + Mutex::Autolock _l(mLock); + buffer = buffers[index].buffer; + buffers[index].buffer = 0; + return buffer; +} + +status_t Layer::BufferManager::attachBuffer(size_t index, + const sp<GraphicBuffer>& buffer) +{ + BufferData* const buffers = mBufferData; + Mutex::Autolock _l(mLock); + buffers[index].buffer = buffer; + buffers[index].texture.dirty = true; + return NO_ERROR; +} + +status_t Layer::BufferManager::destroy(EGLDisplay dpy) +{ + BufferData* const buffers = mBufferData; + size_t num; + { // scope for the lock + Mutex::Autolock _l(mLock); + num = mNumBuffers; + for (size_t i=0 ; i<num ; i++) { + buffers[i].buffer = 0; + } + } + for (size_t i=0 ; i<num ; i++) { + destroyTexture(&buffers[i].texture, dpy); + } + destroyTexture(&mFailoverTexture, dpy); + return NO_ERROR; +} + +status_t Layer::BufferManager::initEglImage(EGLDisplay dpy, + const sp<GraphicBuffer>& buffer) +{ + size_t index = mActiveBuffer; + Image& texture(mBufferData[index].texture); + status_t err = mTextureManager.initEglImage(&texture, dpy, buffer); + // if EGLImage fails, we switch to regular texture mode, and we + // free all resources associated with using EGLImages. + if (err == NO_ERROR) { + mFailover = false; + destroyTexture(&mFailoverTexture, dpy); + } else { + mFailover = true; + const size_t num = mNumBuffers; + for (size_t i=0 ; i<num ; i++) { + destroyTexture(&mBufferData[i].texture, dpy); + } + } + return err; +} + +status_t Layer::BufferManager::loadTexture( + const Region& dirty, const GGLSurface& t) +{ + return mTextureManager.loadTexture(&mFailoverTexture, dirty, t); +} + +status_t Layer::BufferManager::destroyTexture(Image* tex, EGLDisplay dpy) +{ + if (tex->name != -1U) { + glDeleteTextures(1, &tex->name); + tex->name = -1U; + } + if (tex->image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, tex->image); + tex->image = EGL_NO_IMAGE_KHR; + } + return NO_ERROR; } // --------------------------------------------------------------------------- @@ -610,20 +716,37 @@ Layer::SurfaceLayer::~SurfaceLayer() { } -sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, int usage) +sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { sp<GraphicBuffer> buffer; sp<Layer> owner(getOwner()); if (owner != 0) { - LOGE_IF(uint32_t(index)>=NUM_BUFFERS, - "getBuffer() index (%d) out of range", index); - if (uint32_t(index) < NUM_BUFFERS) { - buffer = owner->requestBuffer(index, usage); - } + /* + * requestBuffer() cannot be called from the main thread + * as it could cause a dead-lock, since it may have to wait + * on conditions updated my the main thread. + */ + buffer = owner->requestBuffer(index, w, h, format, usage); } return buffer; } +status_t Layer::SurfaceLayer::setBufferCount(int bufferCount) +{ + status_t err = DEAD_OBJECT; + sp<Layer> owner(getOwner()); + if (owner != 0) { + /* + * setBufferCount() cannot be called from the main thread + * as it could cause a dead-lock, since it may have to wait + * on conditions updated my the main thread. + */ + err = owner->setBufferCount(bufferCount); + } + return err; +} + // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h index 743afb4..59603a5 100644 --- a/libs/surfaceflinger/Layer.h +++ b/libs/surfaceflinger/Layer.h @@ -31,6 +31,7 @@ #include "LayerBase.h" #include "Transform.h" +#include "TextureManager.h" namespace android { @@ -41,16 +42,13 @@ class FreezeLock; // --------------------------------------------------------------------------- -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; } - +public: + // lcblk is (almost) only accessed from the main SF thread, in the places + // where it's not, a reference to Client must be held + SharedBufferServer* lcblk; + Layer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i); @@ -59,7 +57,8 @@ public: status_t setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags=0); - void setDrawingSize(uint32_t w, uint32_t h); + void setBufferSize(uint32_t w, uint32_t h); + bool isFixedSize() const; virtual void onDraw(const Region& clip) const; virtual uint32_t doTransaction(uint32_t transactionFlags); @@ -68,30 +67,32 @@ public: virtual void finishPageFlip(); virtual bool needsBlending() const { return mNeedsBlending; } virtual bool needsDithering() const { return mNeedsDithering; } + virtual bool needsFiltering() const; virtual bool isSecure() const { return mSecure; } virtual sp<Surface> createSurface() const; virtual status_t ditch(); + virtual void onRemoved(); // only for debugging - inline sp<GraphicBuffer> getBuffer(int i) { return mBuffers[i]; } + inline sp<GraphicBuffer> getBuffer(int i) const { return mBufferManager.getBuffer(i); } // only for debugging inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } // only for debugging inline PixelFormat pixelFormat() const { return mFormat; } - // 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]; - } - void reloadTexture(const Region& dirty); uint32_t getEffectiveUsage(uint32_t usage) const; - sp<GraphicBuffer> requestBuffer(int index, int usage); - void destroy(); + sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + status_t setBufferCount(int bufferCount); class SurfaceLayer : public LayerBaseClient::Surface { public: @@ -99,7 +100,9 @@ private: SurfaceID id, const sp<Layer>& owner); ~SurfaceLayer(); private: - virtual sp<GraphicBuffer> requestBuffer(int index, int usage); + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + virtual status_t setBufferCount(int bufferCount); sp<Layer> getOwner() const { return static_cast<Layer*>(Surface::getOwner().get()); } @@ -109,22 +112,83 @@ private: sp<Surface> mSurface; bool mSecure; - bool mNoEGLImageForSwBuffers; int32_t mFrontBufferIndex; bool mNeedsBlending; bool mNeedsDithering; Region mPostedDirtyRegion; sp<FreezeLock> mFreezeLock; PixelFormat mFormat; - - // protected by mLock - sp<GraphicBuffer> mBuffers[NUM_BUFFERS]; - Texture mTextures[NUM_BUFFERS]; - sp<GraphicBuffer> mHybridBuffer; - uint32_t mWidth; - uint32_t mHeight; - - mutable Mutex mLock; + + class BufferManager { + static const size_t NUM_BUFFERS = 2; + struct BufferData { + sp<GraphicBuffer> buffer; + Image texture; + }; + // this lock protect mBufferData[].buffer but since there + // is very little contention, we have only one like for + // the whole array, we also use it to protect mNumBuffers. + mutable Mutex mLock; + BufferData mBufferData[SharedBufferStack::NUM_BUFFER_MAX]; + size_t mNumBuffers; + Texture mFailoverTexture; + TextureManager& mTextureManager; + ssize_t mActiveBuffer; + bool mFailover; + static status_t destroyTexture(Image* tex, EGLDisplay dpy); + + public: + static size_t getDefaultBufferCount() { return NUM_BUFFERS; } + BufferManager(TextureManager& tm); + ~BufferManager(); + + // detach/attach buffer from/to given index + sp<GraphicBuffer> detachBuffer(size_t index); + status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer); + + // resize the number of active buffers + status_t resize(size_t size); + + // ---------------------------------------------- + // must be called from GL thread + + // set/get active buffer index + status_t setActiveBufferIndex(size_t index); + size_t getActiveBufferIndex() const; + + // return the active buffer + sp<GraphicBuffer> getActiveBuffer() const; + + // return the active texture (or fail-over) + Texture getActiveTexture() const; + + // frees resources associated with all buffers + status_t destroy(EGLDisplay dpy); + + // load bitmap data into the active buffer + status_t loadTexture(const Region& dirty, const GGLSurface& t); + + // make active buffer an EGLImage if needed + status_t initEglImage(EGLDisplay dpy, + const sp<GraphicBuffer>& buffer); + + // ---------------------------------------------- + // only for debugging + sp<GraphicBuffer> getBuffer(size_t index) const; + }; + + TextureManager mTextureManager; + BufferManager mBufferManager; + + // this lock protects mWidth and mHeight which are accessed from + // the main thread and requestBuffer's binder transaction thread. + mutable Mutex mLock; + uint32_t mWidth; + uint32_t mHeight; + uint32_t mReqWidth; + uint32_t mReqHeight; + uint32_t mReqFormat; + bool mFixedSize; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp index a8b735e..76733a9 100644 --- a/libs/surfaceflinger/LayerBase.cpp +++ b/libs/surfaceflinger/LayerBase.cpp @@ -32,29 +32,21 @@ #include "LayerBase.h" #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" +#include "TextureManager.h" 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), - mTransformed(false), - mUseLinearFiltering(false), + mNeedsFiltering(false), 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()); @@ -221,13 +213,12 @@ uint32_t LayerBase::doTransaction(uint32_t flags) flags |= eVisibleRegion; this->contentDirty = true; - const bool linearFiltering = mUseLinearFiltering; - mUseLinearFiltering = false; + mNeedsFiltering = false; if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { // we may use linear filtering, if the matrix scales us const uint8_t type = temp.transform.getType(); if (!temp.transform.preserveRects() || (type >= Transform::SCALE)) { - mUseLinearFiltering = true; + mNeedsFiltering = true; } } } @@ -267,7 +258,6 @@ void LayerBase::validateVisibility(const Transform& planeTransform) // cache a few things... mOrientation = tr.getOrientation(); mTransformedBounds = tr.makeBounds(w, h); - mTransformed = transformed; mLeft = tr.tx(); mTop = tr.ty(); } @@ -348,25 +338,13 @@ void LayerBase::draw(const Region& inClip) const */ } -GLuint LayerBase::createTexture() const -{ - GLuint textureName = -1; - glGenTextures(1, &textureName); - glBindTexture(GL_TEXTURE_2D, textureName); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - return textureName; -} - -void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red, - GLclampx green, GLclampx blue, - GLclampx alpha) const +void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red, + GLclampf green, GLclampf blue, + GLclampf alpha) const { const DisplayHardware& hw(graphicPlane(0).displayHardware()); const uint32_t fbHeight = hw.getHeight(); - glColor4x(red,green,blue,alpha); + glColor4f(red,green,blue,alpha); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDisable(GL_DITHER); @@ -374,7 +352,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 +396,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 +415,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 +446,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++; @@ -487,7 +463,7 @@ void LayerBase::validateTexture(GLint textureName) const glBindTexture(GL_TEXTURE_2D, textureName); // TODO: reload the texture if needed // this is currently done in loadTexture() below - if (mUseLinearFiltering) { + if (needsFiltering()) { glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else { @@ -502,200 +478,32 @@ void LayerBase::validateTexture(GLint textureName) const } } -bool LayerBase::isSupportedYuvFormat(int format) const +void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const { - switch (format) { - case HAL_PIXEL_FORMAT_YCbCr_422_SP: - case HAL_PIXEL_FORMAT_YCbCr_420_SP: - case HAL_PIXEL_FORMAT_YCbCr_422_P: - case HAL_PIXEL_FORMAT_YCbCr_420_P: - case HAL_PIXEL_FORMAT_YCbCr_422_I: - case HAL_PIXEL_FORMAT_YCbCr_420_I: - case HAL_PIXEL_FORMAT_YCrCb_420_SP: - return true; - } - return false; -} - -void LayerBase::loadTexture(Texture* texture, - const Region& dirty, const GGLSurface& t) const -{ - if (texture->name == -1U) { - // uh? - return; - } - - glBindTexture(GL_TEXTURE_2D, texture->name); - - /* - * In OpenGL ES we can't specify a stride with glTexImage2D (however, - * GL_UNPACK_ALIGNMENT is a limited form of stride). - * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we - * need to do something reasonable (here creating a bigger texture). - * - * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); - * - * This situation doesn't happen often, but some h/w have a limitation - * for their framebuffer (eg: must be multiple of 8 pixels), and - * we need to take that into account when using these buffers as - * textures. - * - * This should never be a problem with POT textures - */ - - int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format)); - unpack = 1 << ((unpack > 3) ? 3 : unpack); - glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); - - /* - * round to POT if needed - */ - if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { - texture->NPOTAdjust = true; - } - - if (texture->NPOTAdjust) { - // find the smallest power-of-two that will accommodate our surface - texture->potWidth = 1 << (31 - clz(t.width)); - texture->potHeight = 1 << (31 - clz(t.height)); - if (texture->potWidth < t.width) texture->potWidth <<= 1; - if (texture->potHeight < t.height) texture->potHeight <<= 1; - texture->wScale = float(t.width) / texture->potWidth; - texture->hScale = float(t.height) / texture->potHeight; - } else { - texture->potWidth = t.width; - texture->potHeight = t.height; - } - - Rect bounds(dirty.bounds()); - GLvoid* data = 0; - if (texture->width != t.width || texture->height != t.height) { - texture->width = t.width; - texture->height = t.height; - - // texture size changed, we need to create a new one - bounds.set(Rect(t.width, t.height)); - if (t.width == texture->potWidth && - t.height == texture->potHeight) { - // we can do it one pass - data = t.data; - } - - if (t.format == HAL_PIXEL_FORMAT_RGB_565) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGB, texture->potWidth, texture->potHeight, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, texture->potWidth, texture->potHeight, 0, - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || - t.format == HAL_PIXEL_FORMAT_RGBX_8888) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, texture->potWidth, texture->potHeight, 0, - GL_RGBA, GL_UNSIGNED_BYTE, data); - } else if (isSupportedYuvFormat(t.format)) { - // just show the Y plane of YUV buffers - glTexImage2D(GL_TEXTURE_2D, 0, - GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, data); - } else { - // oops, we don't handle this format! - LOGE("layer %p, texture=%d, using format %d, which is not " - "supported by the GL", this, texture->name, t.format); - } - } - if (!data) { - if (t.format == HAL_PIXEL_FORMAT_RGB_565) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, - t.data + bounds.top*t.stride*2); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, - t.data + bounds.top*t.stride*2); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || - t.format == HAL_PIXEL_FORMAT_RGBX_8888) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGBA, GL_UNSIGNED_BYTE, - t.data + bounds.top*t.stride*4); - } else if (isSupportedYuvFormat(t.format)) { - // just show the Y plane of YUV buffers - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_LUMINANCE, GL_UNSIGNED_BYTE, - t.data + bounds.top*t.stride); - } - } -} - -status_t LayerBase::initializeEglImage( - const sp<GraphicBuffer>& buffer, Texture* texture) -{ - status_t err = NO_ERROR; - - // we need to recreate the texture - EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); - - // free the previous image - if (texture->image != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(dpy, texture->image); - texture->image = EGL_NO_IMAGE_KHR; - } - - // construct an EGL_NATIVE_BUFFER_ANDROID - android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); - - // create the new EGLImageKHR - const EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, - EGL_NONE, EGL_NONE - }; - texture->image = eglCreateImageKHR( - dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - (EGLClientBuffer)clientBuf, attrs); - - if (texture->image != EGL_NO_IMAGE_KHR) { - glBindTexture(GL_TEXTURE_2D, texture->name); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, - (GLeglImageOES)texture->image); - GLint error = glGetError(); - if (UNLIKELY(error != GL_NO_ERROR)) { - LOGE("layer=%p, glEGLImageTargetTexture2DOES(%p) " - "failed err=0x%04x", - this, texture->image, error); - err = INVALID_OPERATION; - } else { - // Everything went okay! - texture->NPOTAdjust = false; - texture->dirty = false; - texture->width = clientBuf->width; - texture->height = clientBuf->height; - } - } else { - LOGE("layer=%p, eglCreateImageKHR() failed. err=0x%4x", - this, eglGetError()); - err = INVALID_OPERATION; - } - return err; + 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); } - // --------------------------------------------------------------------------- -int32_t LayerBaseClient::sIdentity = 0; +int32_t LayerBaseClient::sIdentity = 1; LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i) - : LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i), + : LayerBase(flinger, display), client(client), mIndex(i), mIdentity(uint32_t(android_atomic_inc(&sIdentity))) { - lcblk = new SharedBufferServer( - client->ctrlblk, i, NUM_BUFFERS, - mIdentity); } void LayerBaseClient::onFirstRef() @@ -712,16 +520,15 @@ LayerBaseClient::~LayerBaseClient() if (client != 0) { client->free(mIndex); } - 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() @@ -742,12 +549,19 @@ sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const const_cast<LayerBaseClient *>(this)); } -// called with SurfaceFlinger::mStateLock as soon as the layer is entered -// in the purgatory list -void LayerBaseClient::onRemoved() +void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const { - // wake up the condition - lcblk->setStatus(NO_INIT); + 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); } // --------------------------------------------------------------------------- @@ -799,11 +613,17 @@ status_t LayerBaseClient::Surface::onTransact( return BnSurface::onTransact(code, data, reply, flags); } -sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int index, int usage) +sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { return NULL; } +status_t LayerBaseClient::Surface::setBufferCount(int bufferCount) +{ + return INVALID_OPERATION; +} + status_t LayerBaseClient::Surface::registerBuffers( const ISurface::BufferHeap& buffers) { diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h index 62ec839..2e2f2df 100644 --- a/libs/surfaceflinger/LayerBase.h +++ b/libs/surfaceflinger/LayerBase.h @@ -29,7 +29,7 @@ #include <ui/Region.h> #include <ui/Overlay.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> #include <private/surfaceflinger/SharedBufferStack.h> #include <private/surfaceflinger/LayerState.h> @@ -46,40 +46,15 @@ class Client; class GraphicBuffer; class GraphicPlane; class SurfaceFlinger; +class Texture; // --------------------------------------------------------------------------- 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 +100,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(). @@ -199,9 +177,9 @@ public: virtual bool needsDithering() const { return false; } /** - * transformed -- true is this surface needs a to be transformed + * needsLinearFiltering - true if this surface needs filtering */ - virtual bool transformed() const { return mTransformed; } + virtual bool needsFiltering() const { return mNeedsFiltering; } /** * isSecure - true if this surface is secure, that is if it prevents @@ -217,7 +195,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, }; @@ -241,44 +222,18 @@ protected: const GraphicPlane& graphicPlane(int dpy) const; GraphicPlane& graphicPlane(int dpy); - GLuint createTexture() const; - - struct Texture { - Texture() : name(-1U), width(0), height(0), - image(EGL_NO_IMAGE_KHR), transform(0), - NPOTAdjust(false), dirty(true) { } - GLuint name; - GLuint width; - GLuint height; - GLuint potWidth; - GLuint potHeight; - GLfloat wScale; - GLfloat hScale; - EGLImageKHR image; - uint32_t transform; - bool NPOTAdjust; - bool dirty; - }; - - void clearWithOpenGL(const Region& clip, GLclampx r, GLclampx g, - GLclampx b, GLclampx alpha) const; + void clearWithOpenGL(const Region& clip, GLclampf r, GLclampf g, + GLclampf b, GLclampf alpha) const; void clearWithOpenGL(const Region& clip) const; void drawWithOpenGL(const Region& clip, const Texture& texture) const; - void loadTexture(Texture* texture, - const Region& dirty, const GGLSurface& t) const; - status_t initializeEglImage( - const sp<GraphicBuffer>& buffer, Texture* texture); - - bool isSupportedYuvFormat(int format) const; sp<SurfaceFlinger> mFlinger; uint32_t mFlags; // cached during validateVisibility() - bool mTransformed; - bool mUseLinearFiltering; + bool mNeedsFiltering; int32_t mOrientation; - GLfixed mVertices[4][2]; + GLfloat mVertices[4][2]; Rect mTransformedBounds; int mLeft; int mTop; @@ -313,14 +268,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 - SharedBufferServer* lcblk; LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i); @@ -331,14 +278,11 @@ 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 const char* getTypeId() const { return "LayerBaseClient"; } class Surface : public BnSurface { @@ -356,7 +300,10 @@ public: sp<LayerBaseClient> getOwner() const; private: - virtual sp<GraphicBuffer> requestBuffer(int index, int usage); + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + virtual status_t setBufferCount(int bufferCount); + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); @@ -373,8 +320,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 5fd7904..09c90e8 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), @@ -100,7 +95,9 @@ void LayerBlur::unlockPageFlip(const Transform& planeTransform, Region& outDirty mCacheDirty = false; } else { if (!mAutoRefreshPending) { - mFlinger->signalDelayedEvent(ms2ns(500)); + mFlinger->postMessageAsync( + new MessageBase(MessageQueue::INVALIDATE), + ms2ns(500)); mAutoRefreshPending = true; } } @@ -206,8 +203,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 +222,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 5b63dec..380f587 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 5c21593..8a582da 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; // --------------------------------------------------------------------------- @@ -120,7 +118,7 @@ uint32_t LayerBuffer::doTransaction(uint32_t flags) source->onTransaction(flags); uint32_t res = LayerBase::doTransaction(flags); // we always want filtering for these surfaces - mUseLinearFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG); + mNeedsFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG); return res; } @@ -145,14 +143,6 @@ void LayerBuffer::onDraw(const Region& clip) const } } -bool LayerBuffer::transformed() const -{ - sp<Source> source(getSource()); - if (LIKELY(source != 0)) - return source->transformed(); - return false; -} - void LayerBuffer::serverDestroy() { sp<Source> source(clearSource()); @@ -321,16 +311,13 @@ void LayerBuffer::Source::postBuffer(ssize_t offset) { } void LayerBuffer::Source::unregisterBuffers() { } -bool LayerBuffer::Source::transformed() const { - return mLayer.mTransformed; -} // --------------------------------------------------------------------------- LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers) : Source(layer), mStatus(NO_ERROR), mBufferSize(0), - mUseEGLImageDirectly(true) + mTextureManager(layer.mFlags) { if (buffers.heap == NULL) { // this is allowed, but in this case, it is illegal to receive @@ -444,11 +431,6 @@ void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer) mBuffer = buffer; } -bool LayerBuffer::BufferSource::transformed() const -{ - return mBufferHeap.transform ? true : Source::transformed(); -} - void LayerBuffer::BufferSource::onDraw(const Region& clip) const { sp<Buffer> ourBuffer(getBuffer()); @@ -462,35 +444,10 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const NativeBuffer src(ourBuffer->getBuffer()); const Rect transformedBounds(mLayer.getTransformedBounds()); - if (UNLIKELY(mTexture.name == -1LU)) { - mTexture.name = mLayer.createTexture(); - } - #if defined(EGL_ANDROID_image_native_buffer) if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) { err = INVALID_OPERATION; if (ourBuffer->supportsCopybit()) { - - // there are constraints on buffers used by the GPU and these may not - // be honored here. We need to change the API so the buffers - // are allocated with gralloc. For now disable this code-path -#if 0 - // First, try to use the buffer as an EGLImage directly - if (mUseEGLImageDirectly) { - // NOTE: Assume the buffer is allocated with the proper USAGE flags - - sp<GraphicBuffer> buffer = new GraphicBuffer( - src.img.w, src.img.h, src.img.format, - GraphicBuffer::USAGE_HW_TEXTURE, - src.img.w, src.img.handle, false); - - err = mLayer.initializeEglImage(buffer, &mTexture); - if (err != NO_ERROR) { - mUseEGLImageDirectly = false; - } - } -#endif - copybit_device_t* copybit = mLayer.mBlitEngine; if (copybit && err != NO_ERROR) { // create our EGLImageKHR the first time @@ -527,7 +484,7 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const t.format = src.img.format; t.data = (GGLubyte*)src.img.base; const Region dirty(Rect(t.width, t.height)); - mLayer.loadTexture(&mTexture, dirty, t); + mTextureManager.loadTexture(&mTexture, dirty, t); } mTexture.transform = mBufferHeap.transform; @@ -569,7 +526,7 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const // figure out if we need linear filtering if (buffers.w * h == buffers.h * w) { // same pixel area, don't use filtering - mLayer.mUseLinearFiltering = false; + mLayer.mNeedsFiltering = false; } // Allocate a temporary buffer and create the corresponding EGLImageKHR @@ -593,7 +550,8 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const dst.crop.r = w; dst.crop.b = h; - err = mLayer.initializeEglImage(buffer, &mTexture); + EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + err = mTextureManager.initEglImage(&mTexture, dpy, buffer); } return err; @@ -609,7 +567,6 @@ void LayerBuffer::BufferSource::clearTempBufferImage() const glDeleteTextures(1, &mTexture.name); Texture defaultTexture; mTexture = defaultTexture; - mTexture.name = mLayer.createTexture(); } // --------------------------------------------------------------------------- @@ -665,9 +622,9 @@ LayerBuffer::OverlaySource::~OverlaySource() void LayerBuffer::OverlaySource::onDraw(const Region& clip) const { // this would be where the color-key would be set, should we need it. - GLclampx red = 0; - GLclampx green = 0; - GLclampx blue = 0; + GLclampf red = 0; + GLclampf green = 0; + GLclampf blue = 0; mLayer.clearWithOpenGL(clip, red, green, blue, 0); } diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h index b176623..d38dde2 100644 --- a/libs/surfaceflinger/LayerBuffer.h +++ b/libs/surfaceflinger/LayerBuffer.h @@ -21,6 +21,7 @@ #include <sys/types.h> #include "LayerBase.h" +#include "TextureManager.h" struct copybit_device_t; @@ -45,31 +46,25 @@ class LayerBuffer : public LayerBaseClient virtual void onVisibilityResolved(const Transform& planeTransform); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); - virtual bool transformed() const; virtual void destroy() { } protected: LayerBuffer& mLayer; }; 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(); virtual void onDraw(const Region& clip) const; virtual uint32_t doTransaction(uint32_t flags); virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); - virtual bool transformed() const; status_t registerBuffers(const ISurface::BufferHeap& buffers); void postBuffer(ssize_t offset); @@ -133,7 +128,6 @@ private: virtual void onDraw(const Region& clip) const; virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); - virtual bool transformed() const; virtual void destroy() { } private: status_t initTempBuffer() const; @@ -143,9 +137,9 @@ private: status_t mStatus; ISurface::BufferHeap mBufferHeap; size_t mBufferSize; - mutable LayerBase::Texture mTexture; + mutable Texture mTexture; mutable NativeBuffer mTempBuffer; - mutable bool mUseEGLImageDirectly; + mutable TextureManager mTextureManager; }; class OverlaySource : public Source { diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp index fd61e30..568fedb 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 d4672a1..19a9990 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/MessageQueue.cpp b/libs/surfaceflinger/MessageQueue.cpp index b43d801..d668e88 100644 --- a/libs/surfaceflinger/MessageQueue.cpp +++ b/libs/surfaceflinger/MessageQueue.cpp @@ -60,9 +60,9 @@ MessageQueue::~MessageQueue() { } -MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout) +sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout) { - MessageList::value_type result; + sp<MessageBase> result; bool again; do { @@ -132,6 +132,7 @@ MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout) if (again) { // the message has been processed. release our reference to it // without holding the lock. + result->notify(); result = 0; } @@ -141,7 +142,7 @@ MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout) } status_t MessageQueue::postMessage( - const MessageList::value_type& message, nsecs_t relTime, uint32_t flags) + const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags) { return queueMessage(message, relTime, flags); } @@ -154,7 +155,7 @@ status_t MessageQueue::invalidate() { } status_t MessageQueue::queueMessage( - const MessageList::value_type& message, nsecs_t relTime, uint32_t flags) + const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags) { Mutex::Autolock _l(mLock); message->when = systemTime() + relTime; @@ -167,13 +168,13 @@ status_t MessageQueue::queueMessage( return NO_ERROR; } -void MessageQueue::dump(const MessageList::value_type& message) +void MessageQueue::dump(const sp<MessageBase>& message) { Mutex::Autolock _l(mLock); dumpLocked(message); } -void MessageQueue::dumpLocked(const MessageList::value_type& message) +void MessageQueue::dumpLocked(const sp<MessageBase>& message) { LIST::const_iterator cur(mMessages.begin()); LIST::const_iterator end(mMessages.end()); diff --git a/libs/surfaceflinger/MessageQueue.h b/libs/surfaceflinger/MessageQueue.h index dc8138d..890f809 100644 --- a/libs/surfaceflinger/MessageQueue.h +++ b/libs/surfaceflinger/MessageQueue.h @@ -25,6 +25,7 @@ #include <utils/Timers.h> #include <utils/List.h> +#include "Barrier.h" namespace android { @@ -37,7 +38,6 @@ class MessageList List< sp<MessageBase> > mList; typedef List< sp<MessageBase> > LIST; public: - typedef sp<MessageBase> value_type; inline LIST::iterator begin() { return mList.begin(); } inline LIST::const_iterator begin() const { return mList.begin(); } inline LIST::iterator end() { return mList.end(); } @@ -63,11 +63,19 @@ public: // return true if message has a handler virtual bool handler() { return false; } + + // waits for the handler to be processed + void wait() const { barrier.wait(); } + // releases all waiters. this is done automatically if + // handler returns true + void notify() const { barrier.open(); } + protected: virtual ~MessageBase() { } private: + mutable Barrier barrier; friend class LightRefBase<MessageBase>; }; @@ -82,42 +90,33 @@ class MessageQueue typedef List< sp<MessageBase> > LIST; public: - // this is a work-around the multichar constant warning. A macro would - // work too, but would pollute the namespace. - template <int a, int b, int c, int d> - struct WHAT { - static const uint32_t Value = - (uint32_t(a&0xff)<<24)|(uint32_t(b&0xff)<<16)| - (uint32_t(c&0xff)<<8)|uint32_t(d&0xff); - }; - MessageQueue(); ~MessageQueue(); // pre-defined messages enum { - INVALIDATE = WHAT<'_','p','d','t'>::Value + INVALIDATE = '_upd' }; - MessageList::value_type waitMessage(nsecs_t timeout = -1); + sp<MessageBase> waitMessage(nsecs_t timeout = -1); - status_t postMessage(const MessageList::value_type& message, + status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0, uint32_t flags = 0); - + status_t invalidate(); - void dump(const MessageList::value_type& message); + void dump(const sp<MessageBase>& message); private: - status_t queueMessage(const MessageList::value_type& message, + status_t queueMessage(const sp<MessageBase>& message, nsecs_t reltime, uint32_t flags); - void dumpLocked(const MessageList::value_type& message); + void dumpLocked(const sp<MessageBase>& message); Mutex mLock; Condition mCondition; MessageList mMessages; bool mInvalidate; - MessageList::value_type mInvalidateMessage; + sp<MessageBase> mInvalidateMessage; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 0722fda..fff0853 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() @@ -225,7 +225,7 @@ sp<IMemoryHeap> SurfaceFlinger::getCblk() const return mServerHeap; } -sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection() +sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() { Mutex::Autolock _l(mStateLock); uint32_t token = mTokens.acquire(); @@ -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); @@ -427,7 +426,7 @@ void SurfaceFlinger::waitForEvent() timeout = waitTime>0 ? waitTime : 0; } - MessageList::value_type msg = mEventQueue.waitMessage(timeout); + sp<MessageBase> msg = mEventQueue.waitMessage(timeout); // see if we timed out if (isFrozen()) { @@ -462,9 +461,20 @@ void SurfaceFlinger::signal() const { const_cast<SurfaceFlinger*>(this)->signalEvent(); } -void SurfaceFlinger::signalDelayedEvent(nsecs_t delay) +status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg, + nsecs_t reltime, uint32_t flags) { - mEventQueue.postMessage( new MessageBase(MessageQueue::INVALIDATE), delay); + return mEventQueue.postMessage(msg, reltime, flags); +} + +status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg, + nsecs_t reltime, uint32_t flags) +{ + status_t res = mEventQueue.postMessage(msg, reltime, flags); + if (res == NO_ERROR) { + msg->wait(); + } + return res; } // ---------------------------------------------------------------------------- @@ -1079,15 +1089,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 +1105,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; } @@ -1137,15 +1146,11 @@ uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) return android_atomic_and(~flags, &mTransactionFlags) & flags; } -uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, nsecs_t delay) +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) { uint32_t old = android_atomic_or(flags, &mTransactionFlags); if ((old & flags)==0) { // wake the server up - if (delay > 0) { - signalDelayedEvent(delay); - } else { - signalEvent(); - } + signalEvent(); } return old; } @@ -1225,7 +1230,7 @@ int SurfaceFlinger::setOrientation(DisplayID dpy, } sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, - const String8& name, ISurfaceFlingerClient::surface_data_t* params, + const String8& name, ISurfaceComposerClient::surface_data_t* params, DisplayID d, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { @@ -1247,7 +1252,7 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, //LOGD("createSurface for pid %d (%d x %d)", pid, w, h); int32_t id = client->generateId(pid); - if (uint32_t(id) >= NUM_LAYERS_MAX) { + if (uint32_t(id) >= SharedBufferStack::NUM_LAYERS_MAX) { LOGE("createSurface() failed, generateId = %d", id); return surfaceHandle; } @@ -1298,7 +1303,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 +1312,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 +1327,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 +1338,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 +1349,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; } @@ -1397,7 +1406,7 @@ status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer) } }; - mEventQueue.postMessage( new MessageDestroySurface(this, layer) ); + postMessageAsync( new MessageDestroySurface(this, layer) ); return NO_ERROR; } @@ -1512,83 +1521,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 +1544,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()); @@ -1733,7 +1679,7 @@ Client::~Client() { int32_t Client::generateId(int pid) { const uint32_t i = clz( ~mBitmap ); - if (i >= NUM_LAYERS_MAX) { + if (i >= SharedBufferStack::NUM_LAYERS_MAX) { return NO_MEMORY; } mPid = pid; @@ -1760,7 +1706,8 @@ void Client::free(int32_t id) } bool Client::isValid(int32_t i) const { - return (uint32_t(i)<NUM_LAYERS_MAX) && (mBitmap & (1<<(31-i))); + return (uint32_t(i)<SharedBufferStack::NUM_LAYERS_MAX) && + (mBitmap & (1<<(31-i))); } sp<LayerBaseClient> Client::getLayerUser(int32_t i) const { @@ -1773,10 +1720,6 @@ sp<LayerBaseClient> Client::getLayerUser(int32_t i) const { return lbc; } -void Client::dump(const char* what) -{ -} - // --------------------------------------------------------------------------- #if 0 #pragma mark - @@ -1797,7 +1740,7 @@ sp<IMemoryHeap> BClient::getControlBlock() const { } sp<ISurface> BClient::createSurface( - ISurfaceFlingerClient::surface_data_t* params, int pid, + ISurfaceComposerClient::surface_data_t* params, int pid, const String8& name, DisplayID display, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index d75dc15..d8fe98c 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -32,7 +32,7 @@ #include <ui/PixelFormat.h> #include <surfaceflinger/ISurfaceComposer.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> #include "Barrier.h" #include "Layer.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; @@ -159,7 +158,7 @@ public: virtual status_t dump(int fd, const Vector<String16>& args); // ISurfaceComposer interface - virtual sp<ISurfaceFlingerClient> createConnection(); + virtual sp<ISurfaceComposerClient> createConnection(); virtual sp<IMemoryHeap> getCblk() const; virtual void bootFinished(); virtual void openGlobalTransaction(); @@ -190,7 +189,7 @@ private: friend class LayerDim; sp<ISurface> createSurface(ClientID client, int pid, const String8& name, - ISurfaceFlingerClient::surface_data_t* params, + ISurfaceComposerClient::surface_data_t* params, DisplayID display, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags); @@ -256,8 +255,6 @@ private: public: // hack to work around gcc 4.0.3 bug void signalEvent(); private: - void signalDelayedEvent(nsecs_t delay); - void handleConsoleEvents(); void handleTransaction(uint32_t transactionFlags); void handleTransactionLocked( @@ -281,12 +278,13 @@ 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(); uint32_t getTransactionFlags(uint32_t flags); - uint32_t setTransactionFlags(uint32_t flags, nsecs_t delay = 0); + uint32_t setTransactionFlags(uint32_t flags); void commitTransaction(); @@ -310,7 +308,12 @@ private: mutable MessageQueue mEventQueue; - + + status_t postMessageAsync(const sp<MessageBase>& msg, + nsecs_t reltime=0, uint32_t flags = 0); + + status_t postMessageSync(const sp<MessageBase>& msg, + nsecs_t reltime=0, uint32_t flags = 0); // access must be protected by mStateLock @@ -390,14 +393,14 @@ public: // --------------------------------------------------------------------------- -class BClient : public BnSurfaceFlingerClient +class BClient : public BnSurfaceComposerClient { public: BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk); ~BClient(); - // ISurfaceFlingerClient interface + // ISurfaceComposerClient interface virtual sp<IMemoryHeap> getControlBlock() const; virtual sp<ISurface> createSurface( diff --git a/libs/surfaceflinger/TextureManager.cpp b/libs/surfaceflinger/TextureManager.cpp new file mode 100644 index 0000000..ee2159b --- /dev/null +++ b/libs/surfaceflinger/TextureManager.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/GraphicBuffer.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include <hardware/hardware.h> + +#include "clz.h" +#include "DisplayHardware/DisplayHardware.h" +#include "TextureManager.h" + +namespace android { + +// --------------------------------------------------------------------------- + +TextureManager::TextureManager(uint32_t flags) + : mFlags(flags) +{ +} + +GLuint TextureManager::createTexture() +{ + GLuint textureName = -1; + glGenTextures(1, &textureName); + glBindTexture(GL_TEXTURE_2D, textureName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + return textureName; +} + +bool TextureManager::isSupportedYuvFormat(int format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_P: + case HAL_PIXEL_FORMAT_YCbCr_420_P: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + case HAL_PIXEL_FORMAT_YCbCr_420_I: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + return true; + } + return false; +} + +status_t TextureManager::initEglImage(Image* texture, + EGLDisplay dpy, const sp<GraphicBuffer>& buffer) +{ + status_t err = NO_ERROR; + if (!texture->dirty) return err; + + // free the previous image + if (texture->image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, texture->image); + texture->image = EGL_NO_IMAGE_KHR; + } + + // construct an EGL_NATIVE_BUFFER_ANDROID + android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); + + // create the new EGLImageKHR + const EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE, EGL_NONE + }; + texture->image = eglCreateImageKHR( + dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + (EGLClientBuffer)clientBuf, attrs); + + if (texture->image != EGL_NO_IMAGE_KHR) { + if (texture->name == -1UL) { + texture->name = createTexture(); + texture->width = 0; + texture->height = 0; + } + glBindTexture(GL_TEXTURE_2D, texture->name); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, + (GLeglImageOES)texture->image); + GLint error = glGetError(); + if (error != GL_NO_ERROR) { + LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x", + texture->image, error); + err = INVALID_OPERATION; + } else { + // Everything went okay! + texture->dirty = false; + texture->width = clientBuf->width; + texture->height = clientBuf->height; + } + } else { + LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); + err = INVALID_OPERATION; + } + return err; +} + +status_t TextureManager::loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t) +{ + if (texture->name == -1UL) { + texture->name = createTexture(); + texture->width = 0; + texture->height = 0; + } + + glBindTexture(GL_TEXTURE_2D, texture->name); + + /* + * In OpenGL ES we can't specify a stride with glTexImage2D (however, + * GL_UNPACK_ALIGNMENT is a limited form of stride). + * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we + * need to do something reasonable (here creating a bigger texture). + * + * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); + * + * This situation doesn't happen often, but some h/w have a limitation + * for their framebuffer (eg: must be multiple of 8 pixels), and + * we need to take that into account when using these buffers as + * textures. + * + * This should never be a problem with POT textures + */ + + int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format)); + unpack = 1 << ((unpack > 3) ? 3 : unpack); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); + + /* + * round to POT if needed + */ + if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { + texture->NPOTAdjust = true; + } + + if (texture->NPOTAdjust) { + // find the smallest power-of-two that will accommodate our surface + texture->potWidth = 1 << (31 - clz(t.width)); + texture->potHeight = 1 << (31 - clz(t.height)); + if (texture->potWidth < t.width) texture->potWidth <<= 1; + if (texture->potHeight < t.height) texture->potHeight <<= 1; + texture->wScale = float(t.width) / texture->potWidth; + texture->hScale = float(t.height) / texture->potHeight; + } else { + texture->potWidth = t.width; + texture->potHeight = t.height; + } + + Rect bounds(dirty.bounds()); + GLvoid* data = 0; + if (texture->width != t.width || texture->height != t.height) { + texture->width = t.width; + texture->height = t.height; + + // texture size changed, we need to create a new one + bounds.set(Rect(t.width, t.height)); + if (t.width == texture->potWidth && + t.height == texture->potHeight) { + // we can do it one pass + data = t.data; + } + + if (t.format == HAL_PIXEL_FORMAT_RGB_565) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGB, texture->potWidth, texture->potHeight, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || + t.format == HAL_PIXEL_FORMAT_RGBX_8888) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + } else if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + glTexImage2D(GL_TEXTURE_2D, 0, + GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + } else { + // oops, we don't handle this format! + LOGE("texture=%d, using format %d, which is not " + "supported by the GL", texture->name, t.format); + } + } + if (!data) { + if (t.format == HAL_PIXEL_FORMAT_RGB_565) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + t.data + bounds.top*t.stride*2); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, + t.data + bounds.top*t.stride*2); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || + t.format == HAL_PIXEL_FORMAT_RGBX_8888) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.stride*4); + } else if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_LUMINANCE, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.stride); + } + } + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/TextureManager.h b/libs/surfaceflinger/TextureManager.h new file mode 100644 index 0000000..d0acfe9 --- /dev/null +++ b/libs/surfaceflinger/TextureManager.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_TEXTURE_MANAGER_H +#define ANDROID_TEXTURE_MANAGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> + +#include <ui/Region.h> + +#include <pixelflinger/pixelflinger.h> + +namespace android { + +// --------------------------------------------------------------------------- + +class GraphicBuffer; + +// --------------------------------------------------------------------------- + +struct Image { + Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0), + transform(0), dirty(true) { } + GLuint name; + EGLImageKHR image; + GLuint width; + GLuint height; + uint32_t transform; + bool dirty; +}; + +struct Texture : public Image { + Texture() : Image(), NPOTAdjust(false) { } + GLuint potWidth; + GLuint potHeight; + GLfloat wScale; + GLfloat hScale; + bool NPOTAdjust; +}; + +// --------------------------------------------------------------------------- + +class TextureManager { + uint32_t mFlags; + GLuint createTexture(); + static bool isSupportedYuvFormat(int format); +public: + + TextureManager(uint32_t flags); + + // load bitmap data into the active buffer + status_t loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t); + + // make active buffer an EGLImage if needed + status_t initEglImage(Image* texture, + EGLDisplay dpy, const sp<GraphicBuffer>& buffer); +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_TEXTURE_MANAGER_H diff --git a/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp index 175f989..5e27cc9 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 2e5b893..20fa11a 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/Android.mk b/libs/surfaceflinger_client/Android.mk index fe85b34..ce3c71a 100644 --- a/libs/surfaceflinger_client/Android.mk +++ b/libs/surfaceflinger_client/Android.mk @@ -4,7 +4,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ ISurfaceComposer.cpp \ ISurface.cpp \ - ISurfaceFlingerClient.cpp \ + ISurfaceComposerClient.cpp \ LayerState.cpp \ SharedBufferStack.cpp \ Surface.cpp \ diff --git a/libs/surfaceflinger_client/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp index bb86199..7049d9e 100644 --- a/libs/surfaceflinger_client/ISurface.cpp +++ b/libs/surfaceflinger_client/ISurface.cpp @@ -71,11 +71,15 @@ public: { } - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { Parcel data, reply; data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); data.writeInt32(bufferIdx); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); data.writeInt32(usage); remote()->transact(REQUEST_BUFFER, data, &reply); sp<GraphicBuffer> buffer = new GraphicBuffer(); @@ -83,6 +87,16 @@ public: return buffer; } + virtual status_t setBufferCount(int bufferCount) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + remote()->transact(SET_BUFFER_COUNT, data, &reply); + status_t err = reply.readInt32(); + return err; + } + virtual status_t registerBuffers(const BufferHeap& buffers) { Parcel data, reply; @@ -140,12 +154,22 @@ status_t BnSurface::onTransact( case REQUEST_BUFFER: { CHECK_INTERFACE(ISurface, data, reply); int bufferIdx = data.readInt32(); - int usage = data.readInt32(); - sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, usage)); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t format = data.readInt32(); + uint32_t usage = data.readInt32(); + sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format, usage)); if (buffer == NULL) return BAD_VALUE; return reply->write(*buffer); } + case SET_BUFFER_COUNT: { + CHECK_INTERFACE(ISurface, data, reply); + int bufferCount = data.readInt32(); + status_t err = setBufferCount(bufferCount); + reply->writeInt32(err); + return NO_ERROR; + } case REGISTER_BUFFERS: { CHECK_INTERFACE(ISurface, data, reply); BufferHeap buffer; diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp index b6f4e24..50495c1 100644 --- a/libs/surfaceflinger_client/ISurfaceComposer.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp @@ -46,13 +46,13 @@ public: { } - virtual sp<ISurfaceFlingerClient> createConnection() + virtual sp<ISurfaceComposerClient> createConnection() { uint32_t n; Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply); - return interface_cast<ISurfaceFlingerClient>(reply.readStrongBinder()); + return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } virtual sp<IMemoryHeap> getCblk() const diff --git a/libs/surfaceflinger_client/ISurfaceFlingerClient.cpp b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp index def96d7..67c7df8 100644 --- a/libs/surfaceflinger_client/ISurfaceFlingerClient.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp @@ -30,7 +30,7 @@ #include <ui/Rect.h> #include <surfaceflinger/ISurface.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> #include <private/surfaceflinger/LayerState.h> // --------------------------------------------------------------------------- @@ -56,18 +56,18 @@ enum { SET_STATE }; -class BpSurfaceFlingerClient : public BpInterface<ISurfaceFlingerClient> +class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient> { public: - BpSurfaceFlingerClient(const sp<IBinder>& impl) - : BpInterface<ISurfaceFlingerClient>(impl) + BpSurfaceComposerClient(const sp<IBinder>& impl) + : BpInterface<ISurfaceComposerClient>(impl) { } virtual sp<IMemoryHeap> getControlBlock() const { Parcel data, reply; - data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); remote()->transact(GET_CBLK, data, &reply); return interface_cast<IMemoryHeap>(reply.readStrongBinder()); } @@ -82,7 +82,7 @@ public: uint32_t flags) { Parcel data, reply; - data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); data.writeInt32(pid); data.writeString8(name); data.writeInt32(display); @@ -94,11 +94,11 @@ public: params->readFromParcel(reply); return interface_cast<ISurface>(reply.readStrongBinder()); } - + virtual status_t destroySurface(SurfaceID sid) { Parcel data, reply; - data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); data.writeInt32(sid); remote()->transact(DESTROY_SURFACE, data, &reply); return reply.readInt32(); @@ -107,7 +107,7 @@ public: virtual status_t setState(int32_t count, const layer_state_t* states) { Parcel data, reply; - data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); + data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); data.writeInt32(count); for (int i=0 ; i<count ; i++) states[i].write(data); @@ -116,18 +116,18 @@ public: } }; -IMPLEMENT_META_INTERFACE(SurfaceFlingerClient, "android.ui.ISurfaceFlingerClient"); +IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient"); // ---------------------------------------------------------------------- -status_t BnSurfaceFlingerClient::onTransact( +status_t BnSurfaceComposerClient::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { // codes that don't require permission check switch(code) { case GET_CBLK: { - CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); sp<IMemoryHeap> ctl(getControlBlock()); reply->writeStrongBinder(ctl->asBinder()); return NO_ERROR; @@ -135,7 +135,7 @@ status_t BnSurfaceFlingerClient::onTransact( } // these must be checked - + IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); @@ -150,10 +150,10 @@ status_t BnSurfaceFlingerClient::onTransact( return PERMISSION_DENIED; } } - + switch(code) { case CREATE_SURFACE: { - CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); surface_data_t params; int32_t pid = data.readInt32(); String8 name = data.readString8(); @@ -169,12 +169,12 @@ status_t BnSurfaceFlingerClient::onTransact( return NO_ERROR; } break; case DESTROY_SURFACE: { - CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); reply->writeInt32( destroySurface( data.readInt32() ) ); return NO_ERROR; } break; case SET_STATE: { - CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); + CHECK_INTERFACE(ISurfaceComposerClient, data, reply); int32_t count = data.readInt32(); layer_state_t* states = new layer_state_t[count]; for (int i=0 ; i<count ; i++) @@ -191,7 +191,7 @@ status_t BnSurfaceFlingerClient::onTransact( // ---------------------------------------------------------------------- -status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& parcel) +status_t ISurfaceComposerClient::surface_data_t::readFromParcel(const Parcel& parcel) { token = parcel.readInt32(); identity = parcel.readInt32(); @@ -201,7 +201,7 @@ status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& par return NO_ERROR; } -status_t ISurfaceFlingerClient::surface_data_t::writeToParcel(Parcel* parcel) const +status_t ISurfaceComposerClient::surface_data_t::writeToParcel(Parcel* parcel) const { parcel->writeInt32(token); parcel->writeInt32(identity); diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp index a17e8ac..8d03145 100644 --- a/libs/surfaceflinger_client/SharedBufferStack.cpp +++ b/libs/surfaceflinger_client/SharedBufferStack.cpp @@ -44,15 +44,11 @@ SharedClient::~SharedClient() { // these functions are used by the clients status_t SharedClient::validate(size_t i) const { - if (uint32_t(i) >= uint32_t(NUM_LAYERS_MAX)) + if (uint32_t(i) >= uint32_t(SharedBufferStack::NUM_LAYERS_MAX)) return BAD_INDEX; return surfaces[i].status; } -uint32_t SharedClient::getIdentity(size_t token) const { - return uint32_t(surfaces[token].identity); -} - // ---------------------------------------------------------------------------- @@ -67,19 +63,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,18 +113,37 @@ 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; } // ---------------------------------------------------------------------------- SharedBufferBase::SharedBufferBase(SharedClient* sharedClient, - int surface, int num, int32_t identity) + int surface, int32_t identity) : mSharedClient(sharedClient), mSharedStack(sharedClient->surfaces + surface), - mNumBuffers(num), mIdentity(identity) + mIdentity(identity) { } @@ -108,16 +151,16 @@ SharedBufferBase::~SharedBufferBase() { } -uint32_t SharedBufferBase::getIdentity() +status_t SharedBufferBase::getStatus() const { SharedBufferStack& stack( *mSharedStack ); - return stack.identity; + return stack.status; } -status_t SharedBufferBase::getStatus() const +int32_t SharedBufferBase::getIdentity() const { SharedBufferStack& stack( *mSharedStack ); - return stack.status; + return stack.identity; } size_t SharedBufferBase::getFrontBuffer() const @@ -132,16 +175,52 @@ String8 SharedBufferBase::dump(char const* prefix) const char buffer[SIZE]; String8 result; SharedBufferStack& stack( *mSharedStack ); - int tail = (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers; snprintf(buffer, SIZE, - "%s[ head=%2d, available=%2d, queued=%2d, tail=%2d ] " - "reallocMask=%08x, inUse=%2d, identity=%d, status=%d\n", - prefix, stack.head, stack.available, stack.queued, tail, + "%s[ head=%2d, available=%2d, queued=%2d ] " + "reallocMask=%08x, inUse=%2d, identity=%d, status=%d", + prefix, stack.head, stack.available, stack.queued, stack.reallocMask, stack.inUse, stack.identity, stack.status); result.append(buffer); + result.append("\n"); return result; } +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 +228,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) >= SharedBufferStack::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]); } // ---------------------------------------------------------------------------- @@ -206,11 +295,12 @@ SharedBufferServer::RetireUpdate::RetireUpdate( : UpdateBase(sbb), numBuffers(numBuffers) { } 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) >= SharedBufferStack::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; @@ -221,16 +311,15 @@ ssize_t SharedBufferServer::RetireUpdate::operator()() { } } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued)); - // update the head pointer - head = ((head+1 >= numBuffers) ? 0 : head+1); - // lock the buffer before advancing head, which automatically unlocks // the buffer we preventively locked upon entering this function - android_atomic_write(head, &stack.inUse); - // advance head + head = (head + 1) % numBuffers; + android_atomic_write(stack.index[head], &stack.inUse); + + // head is only modified here, so we don't need to use cmpxchg android_atomic_write(head, &stack.head); - + // now that head has moved, we can increment the number of available buffers android_atomic_inc(&stack.available); return head; @@ -250,41 +339,31 @@ ssize_t SharedBufferServer::StatusUpdate::operator()() { SharedBufferClient::SharedBufferClient(SharedClient* sharedClient, int surface, int num, int32_t identity) - : SharedBufferBase(sharedClient, surface, num, identity), tail(0) + : SharedBufferBase(sharedClient, surface, identity), + mNumBuffers(num), tail(0), undoDequeueTail(0) { + SharedBufferStack& stack( *mSharedStack ); tail = computeTail(); + queued_head = stack.head; } int32_t SharedBufferClient::computeTail() const { 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; + return (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers; } 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); } - + + RWLock::AutoRLock _rd(mLock); + const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD); //LOGD("[%d] about to dequeue a buffer", @@ -301,9 +380,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 +393,23 @@ ssize_t SharedBufferClient::dequeue() status_t SharedBufferClient::undoDequeue(int buf) { + RWLock::AutoRLock _rd(mLock); + + // 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) { + RWLock::AutoRLock _rd(mLock); + + SharedBufferStack& stack( *mSharedStack ); LockCondition condition(this, buf); status_t err = waitForCondition(condition); return err; @@ -330,53 +417,100 @@ status_t SharedBufferClient::lock(int buf) status_t SharedBufferClient::queue(int buf) { + RWLock::AutoRLock _rd(mLock); + + SharedBufferStack& stack( *mSharedStack ); + + queued_head = (queued_head + 1) % mNumBuffers; + 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<<(31-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.setCrop(buf, crop); +} + +status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg) { SharedBufferStack& stack( *mSharedStack ); - return stack.setDirtyRegion(buffer, reg); + return stack.setDirtyRegion(buf, reg); +} + +status_t SharedBufferClient::setBufferCount( + int bufferCount, const SetBufferCountCallback& ipc) +{ + SharedBufferStack& stack( *mSharedStack ); + if (uint32_t(bufferCount) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + + if (uint32_t(bufferCount) < SharedBufferStack::NUM_BUFFER_MIN) + return BAD_VALUE; + + RWLock::AutoWLock _wr(mLock); + + status_t err = ipc(bufferCount); + if (err == NO_ERROR) { + mNumBuffers = bufferCount; + queued_head = (stack.head + stack.queued) % mNumBuffers; + } + return err; } // ---------------------------------------------------------------------------- SharedBufferServer::SharedBufferServer(SharedClient* sharedClient, int surface, int num, int32_t identity) - : SharedBufferBase(sharedClient, surface, num, identity) + : SharedBufferBase(sharedClient, surface, identity), + mNumBuffers(num) { mSharedStack->init(identity); mSharedStack->head = num-1; 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++) { + mBufferList.add(i); + mSharedStack->index[i] = i; + } } ssize_t SharedBufferServer::retireAndLock() { + RWLock::AutoRLock _l(mLock); + 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) >= SharedBufferStack::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; } @@ -389,11 +523,25 @@ void SharedBufferServer::setStatus(status_t status) } } -status_t SharedBufferServer::reallocate() +status_t SharedBufferServer::reallocateAll() { + RWLock::AutoRLock _l(mLock); + SharedBufferStack& stack( *mSharedStack ); - uint32_t mask = (1<<mNumBuffers)-1; - android_atomic_or(mask, &stack.reallocMask); + uint32_t mask = mBufferList.getMask(); + android_atomic_or(mask, &stack.reallocMask); + return NO_ERROR; +} + +status_t SharedBufferServer::reallocateAllExcept(int buffer) +{ + RWLock::AutoRLock _l(mLock); + + SharedBufferStack& stack( *mSharedStack ); + BufferList temp(mBufferList); + temp.remove(buffer); + uint32_t mask = temp.getMask(); + android_atomic_or(mask, &stack.reallocMask); return NO_ERROR; } @@ -403,17 +551,77 @@ int32_t SharedBufferServer::getQueuedCount() const return stack.queued; } -status_t SharedBufferServer::assertReallocate(int buffer) +status_t SharedBufferServer::assertReallocate(int buf) { - ReallocateCondition condition(this, buffer); + /* + * NOTE: it's safe to hold mLock for read while waiting for + * the ReallocateCondition because that condition is not updated + * by the thread that holds mLock for write. + */ + RWLock::AutoRLock _l(mLock); + + // TODO: need to validate "buf" + 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); +} + +/* + * NOTE: this is not thread-safe on the server-side, meaning + * 'head' cannot move during this operation. The client-side + * can safely operate an usual. + * + */ +status_t SharedBufferServer::resize(int newNumBuffers) +{ + if (uint32_t(newNumBuffers) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + + RWLock::AutoWLock _l(mLock); + + // for now we're not supporting shrinking + const int numBuffers = mNumBuffers; + if (newNumBuffers < numBuffers) + return BAD_VALUE; + + SharedBufferStack& stack( *mSharedStack ); + const int extra = newNumBuffers - numBuffers; + + // read the head, make sure it's valid + int32_t head = stack.head; + if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) + return BAD_VALUE; + + int base = numBuffers; + int32_t avail = stack.available; + int tail = head - avail + 1; + + if (tail >= 0) { + int8_t* const index = const_cast<int8_t*>(stack.index); + const int nb = numBuffers - head; + memmove(&index[head + extra], &index[head], nb); + base = head; + // move head 'extra' ahead, this doesn't impact stack.index[head]; + stack.head = head + extra; + } + stack.available += extra; + + // fill the new free space with unused buffers + BufferList::const_iterator curr(mBufferList.free_begin()); + for (int i=0 ; i<extra ; i++) { + stack.index[base+i] = *curr; + mBufferList.add(*curr); + ++curr; + } + + mNumBuffers = newNumBuffers; + return NO_ERROR; } SharedBufferStack::Statistics SharedBufferServer::getStats() const @@ -422,6 +630,29 @@ SharedBufferStack::Statistics SharedBufferServer::getStats() const return stack.stats; } +// --------------------------------------------------------------------------- +status_t SharedBufferServer::BufferList::add(int value) +{ + if (uint32_t(value) >= mCapacity) + return BAD_VALUE; + uint32_t mask = 1<<(31-value); + if (mList & mask) + return ALREADY_EXISTS; + mList |= mask; + return NO_ERROR; +} + +status_t SharedBufferServer::BufferList::remove(int value) +{ + if (uint32_t(value) >= mCapacity) + return BAD_VALUE; + uint32_t mask = 1<<(31-value); + if (!(mList & mask)) + return NAME_NOT_FOUND; + mList &= ~mask; + return NO_ERROR; +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp index 5dd75c3..ac4b198 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; @@ -107,7 +104,7 @@ static status_t copyBlt( SurfaceControl::SurfaceControl( const sp<SurfaceComposerClient>& client, const sp<ISurface>& surface, - const ISurfaceFlingerClient::surface_data_t& data, + const ISurfaceComposerClient::surface_data_t& data, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) : mClient(client), mSurface(surface), mToken(data.token), mIdentity(data.identity), @@ -154,75 +151,75 @@ bool SurfaceControl::isSameSurface( } status_t SurfaceControl::setLayer(int32_t layer) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setLayer(mToken, layer); } status_t SurfaceControl::setPosition(int32_t x, int32_t y) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setPosition(mToken, x, y); } status_t SurfaceControl::setSize(uint32_t w, uint32_t h) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setSize(mToken, w, h); } status_t SurfaceControl::hide() { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->hide(mToken); } status_t SurfaceControl::show(int32_t layer) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->show(mToken, layer); } status_t SurfaceControl::freeze() { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->freeze(mToken); } status_t SurfaceControl::unfreeze() { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->unfreeze(mToken); } status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setFlags(mToken, flags, mask); } status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setTransparentRegionHint(mToken, transparent); } status_t SurfaceControl::setAlpha(float alpha) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setAlpha(mToken, alpha); } status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy); } status_t SurfaceControl::setFreezeTint(uint32_t tint) { - const sp<SurfaceComposerClient>& client(mClient); status_t err = validate(); if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); return client->setFreezeTint(mToken, tint); } @@ -233,23 +230,6 @@ status_t SurfaceControl::validate() const mToken, mIdentity, mClient.get()); return NO_INIT; } - SharedClient const* cblk = mClient->mControl; - if (cblk == 0) { - LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); - return NO_INIT; - } - status_t err = cblk->validate(mToken); - if (err != NO_ERROR) { - LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", - mToken, mIdentity, err, strerror(-err)); - return err; - } - uint32_t identity = cblk->getIdentity(mToken); - if (mIdentity != identity) { - LOGE("using an invalid surface id=%d, identity=%u should be %d", - mToken, mIdentity, identity); - return NO_INIT; - } return NO_ERROR; } @@ -299,22 +279,22 @@ sp<Surface> SurfaceControl::getSurface() const // ============================================================================ Surface::Surface(const sp<SurfaceControl>& surface) - : mClient(surface->mClient), mSurface(surface->mSurface), + : mSurface(surface->mSurface), mToken(surface->mToken), mIdentity(surface->mIdentity), mFormat(surface->mFormat), mFlags(surface->mFlags), mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL), + mInitCheck(NO_INIT), mWidth(surface->mWidth), mHeight(surface->mHeight) { - mSharedBufferClient = new SharedBufferClient( - mClient->mControl, mToken, 2, mIdentity); - + mClient = new SurfaceClient(surface->mClient); init(); } Surface::Surface(const Parcel& parcel) - : mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL) + : mBufferMapper(GraphicBufferMapper::get()), + mSharedBufferClient(NULL), mInitCheck(NO_INIT) { - sp<IBinder> clientBinder = parcel.readStrongBinder(); + sp<IBinder> conn = parcel.readStrongBinder(); mSurface = interface_cast<ISurface>(parcel.readStrongBinder()); mToken = parcel.readInt32(); mIdentity = parcel.readInt32(); @@ -322,15 +302,7 @@ Surface::Surface(const Parcel& parcel) mHeight = parcel.readInt32(); mFormat = parcel.readInt32(); mFlags = parcel.readInt32(); - - // FIXME: what does that mean if clientBinder is NULL here? - if (clientBinder != NULL) { - mClient = SurfaceComposerClient::clientForConnection(clientBinder); - - mSharedBufferClient = new SharedBufferClient( - mClient->mControl, mToken, 2, mIdentity); - } - + mClient = new SurfaceClient(conn); init(); } @@ -342,7 +314,7 @@ void Surface::init() android_native_window_t::queueBuffer = queueBuffer; android_native_window_t::query = query; android_native_window_t::perform = perform; - mSwapRectangle.makeInvalid(); + DisplayInfo dinfo; SurfaceComposerClient::getDisplayInfo(0, &dinfo); const_cast<float&>(android_native_window_t::xdpi) = dinfo.xdpi; @@ -351,17 +323,27 @@ void Surface::init() const_cast<int&>(android_native_window_t::minSwapInterval) = 1; const_cast<int&>(android_native_window_t::maxSwapInterval) = 1; const_cast<uint32_t&>(android_native_window_t::flags) = 0; - // be default we request a hardware surface - mUsage = GRALLOC_USAGE_HW_RENDER; + mConnected = 0; - mNeedFullUpdate = false; + mSwapRectangle.makeInvalid(); + // two buffers by default + mBuffers.setCapacity(2); + mBuffers.insertAt(0, 2); + + if (mClient != 0 && mClient->initCheck() == NO_ERROR) { + mSharedBufferClient = new SharedBufferClient( + mClient->getSharedClient(), mToken, 2, mIdentity); + } + + mInitCheck = initCheck(); } Surface::~Surface() { // this is a client-side operation, the surface is destroyed, unmap // its buffers in this process. - for (int i=0 ; i<2 ; i++) { + size_t size = mBuffers.size(); + for (size_t i=0 ; i<size ; i++) { if (mBuffers[i] != 0 && mBuffers[i]->handle != 0) { getBufferMapper().unregisterBuffer(mBuffers[i]->handle); } @@ -369,62 +351,78 @@ Surface::~Surface() // clear all references and trigger an IPC now, to make sure things // happen without delay, since these resources are quite heavy. + mBuffers.clear(); mClient.clear(); mSurface.clear(); delete mSharedBufferClient; IPCThreadState::self()->flushCommands(); } -sp<SurfaceComposerClient> Surface::getClient() const { - return mClient; -} - -sp<ISurface> Surface::getISurface() const { - return mSurface; +status_t Surface::initCheck() const +{ + if (mToken<0 || mClient==0 || mClient->initCheck() != NO_ERROR) { + return NO_INIT; + } + SharedClient const* cblk = mClient->getSharedClient(); + if (cblk == 0) { + LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); + return NO_INIT; + } + return cblk->validate(mToken); } bool Surface::isValid() { - return mToken>=0 && mClient!=0; + return mInitCheck == NO_ERROR; } status_t Surface::validate() const { - sp<SurfaceComposerClient> client(getClient()); - if (mToken<0 || mClient==0) { - LOGE("invalid token (%d, identity=%u) or client (%p)", - mToken, mIdentity, client.get()); - return NO_INIT; + // check that we initialized ourself properly + if (mInitCheck != NO_ERROR) { + LOGE("invalid token (%d, identity=%u) or client (%p)", + mToken, mIdentity, mClient.get()); + return mInitCheck; } - SharedClient const* cblk = mClient->mControl; - if (cblk == 0) { - LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); + + // verify the identity of this surface + uint32_t identity = mSharedBufferClient->getIdentity(); + + // this is a bit of a (temporary) special case, identity==0 means that + // no operation are allowed from the client (eg: dequeue/queue), this + // is used with PUSH_BUFFER surfaces for instance + if (identity == 0) { + LOGE("[Surface] invalid operation (identity=%u)", mIdentity); + return INVALID_OPERATION; + } + + if (mIdentity != identity) { + LOGE("[Surface] using an invalid surface id=%d, " + "identity=%u should be %d", + mToken, mIdentity, identity); return NO_INIT; } - status_t err = cblk->validate(mToken); + + // check the surface didn't become invalid + status_t err = mSharedBufferClient->getStatus(); if (err != NO_ERROR) { LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", mToken, mIdentity, err, strerror(-err)); return err; } - uint32_t identity = cblk->getIdentity(mToken); - if (mIdentity != identity) { - LOGE("using an invalid surface id=%d, identity=%u should be %d", - mToken, mIdentity, identity); - return NO_INIT; - } + return NO_ERROR; } - -bool Surface::isSameSurface( - const sp<Surface>& lhs, const sp<Surface>& rhs) -{ +bool Surface::isSameSurface(const sp<Surface>& lhs, const sp<Surface>& rhs) { if (lhs == 0 || rhs == 0) return false; - return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); } +sp<ISurface> Surface::getISurface() const { + return mSurface; +} + // ---------------------------------------------------------------------------- int Surface::setSwapInterval(android_native_window_t* window, int interval) { @@ -467,21 +465,24 @@ 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); +bool Surface::needNewBuffer(int bufIdx, + uint32_t *pWidth, uint32_t *pHeight, + uint32_t *pFormat, uint32_t *pUsage) const +{ + Mutex::Autolock _l(mSurfaceLock); + + // Always call needNewBuffer(), since it clears the needed buffers flags + bool needNewBuffer = mSharedBufferClient->needNewBuffer(bufIdx); + bool validBuffer = mBufferInfo.validateBuffer(mBuffers[bufIdx]); + bool newNeewBuffer = needNewBuffer || !validBuffer; + if (newNeewBuffer) { + mBufferInfo.get(pWidth, pHeight, pFormat, pUsage); } - return err; + return newNeewBuffer; } -// ---------------------------------------------------------------------------- - - int Surface::dequeueBuffer(android_native_buffer_t** buffer) { - sp<SurfaceComposerClient> client(getClient()); status_t err = validate(); if (err != NO_ERROR) return err; @@ -492,24 +493,28 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer) return bufIdx; } - // below we make sure we AT LEAST have the usage flags we want - const uint32_t usage(getUsage()); - const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); - if (backBuffer == 0 || - ((uint32_t(backBuffer->usage) & usage) != usage) || - mSharedBufferClient->needNewBuffer(bufIdx)) - { - err = getBufferLocked(bufIdx, usage); - LOGE_IF(err, "getBufferLocked(%ld, %08x) failed (%s)", - bufIdx, usage, strerror(-err)); + // grow the buffer array if needed + const size_t size = mBuffers.size(); + const size_t needed = bufIdx+1; + if (size < needed) { + mBuffers.insertAt(size, needed-size); + } + + uint32_t w, h, format, usage; + if (needNewBuffer(bufIdx, &w, &h, &format, &usage)) { + err = getBufferLocked(bufIdx, w, h, format, usage); + LOGE_IF(err, "getBufferLocked(%ld, %u, %u, %u, %08x) failed (%s)", + bufIdx, w, h, format, usage, strerror(-err)); if (err == NO_ERROR) { // reset the width/height with the what we get from the buffer + const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); mWidth = uint32_t(backBuffer->width); mHeight = uint32_t(backBuffer->height); } } // if we still don't have a buffer here, we probably ran out of memory + const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); if (!err && backBuffer==0) { err = NO_MEMORY; } @@ -526,12 +531,11 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer) int Surface::lockBuffer(android_native_buffer_t* buffer) { - sp<SurfaceComposerClient> client(getClient()); status_t err = validate(); 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; @@ -539,7 +543,6 @@ int Surface::lockBuffer(android_native_buffer_t* buffer) int Surface::queueBuffer(android_native_buffer_t* buffer) { - sp<SurfaceComposerClient> client(getClient()); status_t err = validate(); if (err != NO_ERROR) return err; @@ -548,14 +551,15 @@ 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)); if (err == NO_ERROR) { // FIXME: can we avoid this IPC if we know there is one pending? - client->signalServer(); + mClient->signalServer(); } return err; } @@ -578,6 +582,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 +597,15 @@ 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; + case NATIVE_WINDOW_SET_BUFFER_COUNT: + res = dispatch_set_buffer_count( args ); + break; + case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: + res = dispatch_set_buffers_geometry( args ); + break; default: res = NAME_NOT_FOUND; break; @@ -608,12 +625,25 @@ 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) ); +} +int Surface::dispatch_set_buffer_count(va_list args) { + size_t bufferCount = va_arg(args, size_t); + return setBufferCount(bufferCount); +} +int Surface::dispatch_set_buffers_geometry(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + int f = va_arg(args, int); + return setBuffersGeometry(w, h, f); +} void Surface::setUsage(uint32_t reqUsage) { Mutex::Autolock _l(mSurfaceLock); - mUsage = reqUsage; + mBufferInfo.set(reqUsage); } int Surface::connect(int api) @@ -654,19 +684,55 @@ int Surface::disconnect(int api) return err; } -uint32_t Surface::getUsage() const +int Surface::crop(Rect const* rect) { Mutex::Autolock _l(mSurfaceLock); - return mUsage; + // TODO: validate rect size + mNextBufferCrop = *rect; + return NO_ERROR; } +int Surface::setBufferCount(int bufferCount) +{ + sp<ISurface> s(mSurface); + if (s == 0) return NO_INIT; + + class SetBufferCountIPC : public SharedBufferClient::SetBufferCountCallback { + sp<ISurface> surface; + virtual status_t operator()(int bufferCount) const { + return surface->setBufferCount(bufferCount); + } + public: + SetBufferCountIPC(const sp<ISurface>& surface) : surface(surface) { } + } ipc(s); + + status_t err = mSharedBufferClient->setBufferCount(bufferCount, ipc); + LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s", + bufferCount, strerror(-err)); + return err; +} + +int Surface::setBuffersGeometry(int w, int h, int format) +{ + if (w<0 || h<0 || format<0) + return BAD_VALUE; + + if ((w && !h) || (!w && h)) + return BAD_VALUE; + + Mutex::Autolock _l(mSurfaceLock); + mBufferInfo.set(w, h, format); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + int Surface::getConnectedApi() const { Mutex::Autolock _l(mSurfaceLock); return mConnected; } - // ---------------------------------------------------------------------------- status_t Surface::lock(SurfaceInfo* info, bool blocking) { @@ -703,45 +769,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 +845,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,7 +857,13 @@ void Surface::setSwapRectangle(const Rect& r) { mSwapRectangle = r; } -status_t Surface::getBufferLocked(int index, int usage) +int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const +{ + return buffer->getIndex(); +} + +status_t Surface::getBufferLocked(int index, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { sp<ISurface> s(mSurface); if (s == 0) return NO_INIT; @@ -797,20 +871,21 @@ status_t Surface::getBufferLocked(int index, int usage) status_t err = NO_MEMORY; // free the current buffer - sp<GraphicBuffer>& currentBuffer(mBuffers[index]); + sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index)); if (currentBuffer != 0) { getBufferMapper().unregisterBuffer(currentBuffer->handle); currentBuffer.clear(); } - sp<GraphicBuffer> buffer = s->requestBuffer(index, usage); + sp<GraphicBuffer> buffer = s->requestBuffer(index, w, h, format, usage); LOGE_IF(buffer==0, "ISurface::getBuffer(%d, %08x) returned NULL", index, usage); if (buffer != 0) { // this should never happen by construction LOGE_IF(buffer->handle == NULL, - "Surface (identity=%d) requestBuffer(%d, %08x) returned" - "a buffer with a null handle", mIdentity, index, usage); + "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) " + "returned a buffer with a null handle", + mIdentity, index, w, h, format, usage); err = mSharedBufferClient->getStatus(); LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err); if (!err && buffer->handle != NULL) { @@ -820,14 +895,52 @@ 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; + err = err<0 ? err : status_t(NO_MEMORY); } } return err; } +// ---------------------------------------------------------------------------- +Surface::BufferInfo::BufferInfo() + : mWidth(0), mHeight(0), mFormat(0), + mUsage(GRALLOC_USAGE_HW_RENDER), mDirty(0) +{ +} + +void Surface::BufferInfo::set(uint32_t w, uint32_t h, uint32_t format) { + if ((mWidth != w) || (mHeight != h) || (mFormat != format)) { + mWidth = w; + mHeight = h; + mFormat = format; + mDirty |= GEOMETRY; + } +} + +void Surface::BufferInfo::set(uint32_t usage) { + mUsage = usage; +} + +void Surface::BufferInfo::get(uint32_t *pWidth, uint32_t *pHeight, + uint32_t *pFormat, uint32_t *pUsage) const { + *pWidth = mWidth; + *pHeight = mHeight; + *pFormat = mFormat; + *pUsage = mUsage; +} + +bool Surface::BufferInfo::validateBuffer(const sp<GraphicBuffer>& buffer) const { + // make sure we AT LEAST have the usage flags we want + if (mDirty || buffer==0 || + ((buffer->usage & mUsage) != mUsage)) { + mDirty = 0; + return false; + } + return true; +} + +// ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp index 85167da..0670d20 100644 --- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp +++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp @@ -17,98 +17,147 @@ #define LOG_TAG "SurfaceComposerClient" #include <stdint.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> #include <sys/types.h> -#include <sys/stat.h> -#include <cutils/memory.h> - -#include <utils/Atomic.h> #include <utils/Errors.h> #include <utils/threads.h> -#include <utils/KeyedVector.h> +#include <utils/SortedVector.h> #include <utils/Log.h> +#include <utils/Singleton.h> #include <binder/IServiceManager.h> #include <binder/IMemory.h> #include <ui/DisplayInfo.h> -#include <ui/Rect.h> #include <surfaceflinger/ISurfaceComposer.h> -#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <surfaceflinger/ISurfaceComposerClient.h> #include <surfaceflinger/ISurface.h> #include <surfaceflinger/SurfaceComposerClient.h> #include <private/surfaceflinger/LayerState.h> #include <private/surfaceflinger/SharedBufferStack.h> -#define VERBOSE(...) ((void)0) -//#define VERBOSE LOGD - -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) namespace android { - // --------------------------------------------------------------------------- -// Must not be holding SurfaceComposerClient::mLock when acquiring gLock here. -static Mutex gLock; -static sp<ISurfaceComposer> gSurfaceManager; -static DefaultKeyedVector< sp<IBinder>, wp<SurfaceComposerClient> > gActiveConnections; -static SortedVector<sp<SurfaceComposerClient> > gOpenTransactions; -static sp<IMemoryHeap> gServerCblkMemory; -static volatile surface_flinger_cblk_t* gServerCblk; - -static sp<ISurfaceComposer> getComposerService() -{ - sp<ISurfaceComposer> sc; - Mutex::Autolock _l(gLock); - if (gSurfaceManager != 0) { - sc = gSurfaceManager; - } else { - // release the lock while we're waiting... - gLock.unlock(); - - sp<IBinder> binder; - sp<IServiceManager> sm = defaultServiceManager(); - do { - binder = sm->getService(String16("SurfaceFlinger")); - if (binder == 0) { - LOGW("SurfaceFlinger not published, waiting..."); - usleep(500000); // 0.5 s - } - } while(binder == 0); - - // grab the lock again for updating gSurfaceManager - gLock.lock(); - if (gSurfaceManager == 0) { - sc = interface_cast<ISurfaceComposer>(binder); - gSurfaceManager = sc; - } else { - sc = gSurfaceManager; +class ComposerService : public Singleton<ComposerService> +{ + // these are constants + sp<ISurfaceComposer> mComposerService; + sp<IMemoryHeap> mServerCblkMemory; + surface_flinger_cblk_t volatile* mServerCblk; + + ComposerService() : Singleton<ComposerService>() { + const String16 name("SurfaceFlinger"); + while (getService(name, &mComposerService) != NO_ERROR) { + usleep(250000); } + mServerCblkMemory = mComposerService->getCblk(); + mServerCblk = static_cast<surface_flinger_cblk_t volatile *>( + mServerCblkMemory->getBase()); + } + + friend class Singleton<ComposerService>; + +public: + static sp<ISurfaceComposer> getComposerService() { + return ComposerService::getInstance().mComposerService; + } + static surface_flinger_cblk_t const volatile * getControlBlock() { + return ComposerService::getInstance().mServerCblk; } - return sc; +}; + +ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService); + + +static inline sp<ISurfaceComposer> getComposerService() { + return ComposerService::getComposerService(); +} + +static inline surface_flinger_cblk_t const volatile * get_cblk() { + return ComposerService::getControlBlock(); } -static volatile surface_flinger_cblk_t const * get_cblk() +// --------------------------------------------------------------------------- + +class Composer : public Singleton<Composer> { - if (gServerCblk == 0) { - sp<ISurfaceComposer> sm(getComposerService()); - Mutex::Autolock _l(gLock); - if (gServerCblk == 0) { - gServerCblkMemory = sm->getCblk(); - LOGE_IF(gServerCblkMemory==0, "Can't get server control block"); - gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->getBase(); - LOGE_IF(gServerCblk==0, "Can't get server control block address"); + Mutex mLock; + SortedVector< wp<SurfaceComposerClient> > mActiveConnections; + SortedVector<sp<SurfaceComposerClient> > mOpenTransactions; + + Composer() : Singleton<Composer>() { + } + + void addClientImpl(const sp<SurfaceComposerClient>& client) { + Mutex::Autolock _l(mLock); + mActiveConnections.add(client); + } + + void removeClientImpl(const sp<SurfaceComposerClient>& client) { + Mutex::Autolock _l(mLock); + mActiveConnections.remove(client); + } + + void openGlobalTransactionImpl() + { + Mutex::Autolock _l(mLock); + if (mOpenTransactions.size()) { + LOGE("openGlobalTransaction() called more than once. skipping."); + return; + } + const size_t N = mActiveConnections.size(); + for (size_t i=0; i<N; i++) { + sp<SurfaceComposerClient> client(mActiveConnections[i].promote()); + if (client != 0 && mOpenTransactions.indexOf(client) < 0) { + if (client->openTransaction() == NO_ERROR) { + mOpenTransactions.add(client); + } else { + LOGE("openTransaction on client %p failed", client.get()); + // let it go, it'll fail later when the user + // tries to do something with the transaction + } + } } } - return gServerCblk; -} + + void closeGlobalTransactionImpl() + { + mLock.lock(); + SortedVector< sp<SurfaceComposerClient> > clients(mOpenTransactions); + mOpenTransactions.clear(); + mLock.unlock(); + + sp<ISurfaceComposer> sm(getComposerService()); + sm->openGlobalTransaction(); + const size_t N = clients.size(); + for (size_t i=0; i<N; i++) { + clients[i]->closeTransaction(); + } + sm->closeGlobalTransaction(); + } + + friend class Singleton<Composer>; + +public: + static void addClient(const sp<SurfaceComposerClient>& client) { + Composer::getInstance().addClientImpl(client); + } + static void removeClient(const sp<SurfaceComposerClient>& client) { + Composer::getInstance().removeClientImpl(client); + } + static void openGlobalTransaction() { + Composer::getInstance().openGlobalTransactionImpl(); + } + static void closeGlobalTransaction() { + Composer::getInstance().closeGlobalTransactionImpl(); + } +}; + +ANDROID_SINGLETON_STATIC_INSTANCE(Composer); // --------------------------------------------------------------------------- @@ -120,61 +169,27 @@ static inline int compare_type( const layer_state_t& lhs, } SurfaceComposerClient::SurfaceComposerClient() + : mTransactionOpen(0), mPrebuiltLayerState(0), mStatus(NO_INIT) { - sp<ISurfaceComposer> sm(getComposerService()); - if (sm == 0) { - _init(0, 0); - return; - } - - _init(sm, sm->createConnection()); - - if (mClient != 0) { - Mutex::Autolock _l(gLock); - VERBOSE("Adding client %p to map", this); - gActiveConnections.add(mClient->asBinder(), this); - } } -SurfaceComposerClient::SurfaceComposerClient( - const sp<ISurfaceComposer>& sm, const sp<IBinder>& conn) -{ - _init(sm, interface_cast<ISurfaceFlingerClient>(conn)); -} - - -status_t SurfaceComposerClient::linkToComposerDeath( - const sp<IBinder::DeathRecipient>& recipient, - void* cookie, uint32_t flags) +void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sm(getComposerService()); - return sm->asBinder()->linkToDeath(recipient, cookie, flags); -} - -void SurfaceComposerClient::_init( - const sp<ISurfaceComposer>& sm, const sp<ISurfaceFlingerClient>& conn) -{ - VERBOSE("Creating client %p, conn %p", this, conn.get()); - - mPrebuiltLayerState = 0; - mTransactionOpen = 0; - mStatus = NO_ERROR; - mControl = 0; - - mClient = conn; - if (mClient == 0) { - mStatus = NO_INIT; - return; + if (sm != 0) { + sp<ISurfaceComposerClient> conn = sm->createConnection(); + if (conn != 0) { + mClient = conn; + Composer::addClient(this); + mPrebuiltLayerState = new layer_state_t; + mStatus = NO_ERROR; + } } - - mControlMemory = mClient->getControlBlock(); - mSignalServer = sm; - mControl = static_cast<SharedClient *>(mControlMemory->getBase()); } SurfaceComposerClient::~SurfaceComposerClient() { - VERBOSE("Destroying client %p, conn %p", this, mClient.get()); + delete mPrebuiltLayerState; dispose(); } @@ -188,69 +203,31 @@ sp<IBinder> SurfaceComposerClient::connection() const return (mClient != 0) ? mClient->asBinder() : 0; } -sp<SurfaceComposerClient> -SurfaceComposerClient::clientForConnection(const sp<IBinder>& conn) +status_t SurfaceComposerClient::linkToComposerDeath( + const sp<IBinder::DeathRecipient>& recipient, + void* cookie, uint32_t flags) { - sp<SurfaceComposerClient> client; - - { // scope for lock - Mutex::Autolock _l(gLock); - client = gActiveConnections.valueFor(conn).promote(); - } - - if (client == 0) { - // Need to make a new client. - sp<ISurfaceComposer> sm(getComposerService()); - client = new SurfaceComposerClient(sm, conn); - if (client != 0 && client->initCheck() == NO_ERROR) { - Mutex::Autolock _l(gLock); - gActiveConnections.add(conn, client); - //LOGD("we have %d connections", gActiveConnections.size()); - } else { - client.clear(); - } - } - - return client; + sp<ISurfaceComposer> sm(getComposerService()); + return sm->asBinder()->linkToDeath(recipient, cookie, flags); } void SurfaceComposerClient::dispose() { // this can be called more than once. - - sp<IMemoryHeap> controlMemory; - sp<ISurfaceFlingerClient> client; - - { - Mutex::Autolock _lg(gLock); - Mutex::Autolock _lm(mLock); - - mSignalServer = 0; - - if (mClient != 0) { - client = mClient; - mClient.clear(); - - ssize_t i = gActiveConnections.indexOfKey(client->asBinder()); - if (i >= 0 && gActiveConnections.valueAt(i) == this) { - VERBOSE("Removing client %p from map at %d", this, int(i)); - gActiveConnections.removeItemsAt(i); - } - } - - delete mPrebuiltLayerState; - mPrebuiltLayerState = 0; - controlMemory = mControlMemory; - mControlMemory.clear(); - mControl = 0; - mStatus = NO_INIT; + sp<ISurfaceComposerClient> client; + Mutex::Autolock _lm(mLock); + if (mClient != 0) { + Composer::removeClient(this); + client = mClient; // hold ref while lock is held + mClient.clear(); } + mStatus = NO_INIT; } status_t SurfaceComposerClient::getDisplayInfo( DisplayID dpy, DisplayInfo* info) { - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); @@ -268,7 +245,7 @@ status_t SurfaceComposerClient::getDisplayInfo( ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) { - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; @@ -277,7 +254,7 @@ ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) { - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; @@ -286,7 +263,7 @@ ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) { - if (uint32_t(dpy)>=NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; @@ -305,12 +282,6 @@ ssize_t SurfaceComposerClient::getNumberOfDisplays() return n; } - -void SurfaceComposerClient::signalServer() -{ - mSignalServer->signal(); -} - sp<SurfaceControl> SurfaceComposerClient::createSurface( int pid, DisplayID display, @@ -327,7 +298,6 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface( return SurfaceComposerClient::createSurface(pid, name, display, w, h, format, flags); - } sp<SurfaceControl> SurfaceComposerClient::createSurface( @@ -341,11 +311,11 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface( { sp<SurfaceControl> result; if (mStatus == NO_ERROR) { - ISurfaceFlingerClient::surface_data_t data; + ISurfaceComposerClient::surface_data_t data; sp<ISurface> surface = mClient->createSurface(&data, pid, name, display, w, h, format, flags); if (surface != 0) { - if (uint32_t(data.token) < NUM_LAYERS_MAX) { + if (uint32_t(data.token) < SharedBufferStack::NUM_LAYERS_MAX) { result = new SurfaceControl(this, surface, data, w, h, format, flags); } } @@ -373,56 +343,14 @@ status_t SurfaceComposerClient::destroySurface(SurfaceID sid) void SurfaceComposerClient::openGlobalTransaction() { - Mutex::Autolock _l(gLock); - - if (gOpenTransactions.size()) { - LOGE("openGlobalTransaction() called more than once. skipping."); - return; - } - - const size_t N = gActiveConnections.size(); - VERBOSE("openGlobalTransaction (%ld clients)", N); - for (size_t i=0; i<N; i++) { - sp<SurfaceComposerClient> client(gActiveConnections.valueAt(i).promote()); - if (client != 0 && gOpenTransactions.indexOf(client) < 0) { - if (client->openTransaction() == NO_ERROR) { - if (gOpenTransactions.add(client) < 0) { - // Ooops! - LOGE( "Unable to add a SurfaceComposerClient " - "to the global transaction set (out of memory?)"); - client->closeTransaction(); - // let it go, it'll fail later when the user - // tries to do something with the transaction - } - } else { - LOGE("openTransaction on client %p failed", client.get()); - // let it go, it'll fail later when the user - // tries to do something with the transaction - } - } - } + Composer::openGlobalTransaction(); } void SurfaceComposerClient::closeGlobalTransaction() { - gLock.lock(); - SortedVector< sp<SurfaceComposerClient> > clients(gOpenTransactions); - gOpenTransactions.clear(); - gLock.unlock(); - - const size_t N = clients.size(); - VERBOSE("closeGlobalTransaction (%ld clients)", N); - - sp<ISurfaceComposer> sm(getComposerService()); - sm->openGlobalTransaction(); - for (size_t i=0; i<N; i++) { - clients[i]->closeTransaction(); - } - sm->closeGlobalTransaction(); - + Composer::closeGlobalTransaction(); } - status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) { sp<ISurfaceComposer> sm(getComposerService()); @@ -447,26 +375,16 @@ status_t SurfaceComposerClient::openTransaction() if (mStatus != NO_ERROR) return mStatus; Mutex::Autolock _l(mLock); - VERBOSE( "openTransaction (client %p, mTransactionOpen=%d)", - this, mTransactionOpen); mTransactionOpen++; - if (mPrebuiltLayerState == 0) { - mPrebuiltLayerState = new layer_state_t; - } return NO_ERROR; } - status_t SurfaceComposerClient::closeTransaction() { if (mStatus != NO_ERROR) return mStatus; Mutex::Autolock _l(mLock); - - VERBOSE( "closeTransaction (client %p, mTransactionOpen=%d)", - this, mTransactionOpen); - if (mTransactionOpen <= 0) { LOGE( "closeTransaction (client %p, mTransactionOpen=%d) " "called more times than openTransaction()", @@ -488,7 +406,7 @@ status_t SurfaceComposerClient::closeTransaction() return NO_ERROR; } -layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index) +layer_state_t* SurfaceComposerClient::get_state_l(SurfaceID index) { // API usage error, do nothing. if (mTransactionOpen<=0) { @@ -498,7 +416,7 @@ layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index) } // use mPrebuiltLayerState just to find out if we already have it - layer_state_t& dummy = *mPrebuiltLayerState; + layer_state_t& dummy(*mPrebuiltLayerState); dummy.surface = index; ssize_t i = mStates.indexOf(dummy); if (i < 0) { @@ -508,49 +426,49 @@ layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index) return mStates.editArray() + i; } -layer_state_t* SurfaceComposerClient::_lockLayerState(SurfaceID id) +layer_state_t* SurfaceComposerClient::lockLayerState(SurfaceID id) { layer_state_t* s; mLock.lock(); - s = _get_state_l(id); + s = get_state_l(id); if (!s) mLock.unlock(); return s; } -void SurfaceComposerClient::_unlockLayerState() +void SurfaceComposerClient::unlockLayerState() { mLock.unlock(); } status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::ePositionChanged; s->x = x; s->y = y; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eSizeChanged; s->w = w; s->h = h; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eLayerChanged; s->z = z; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } @@ -579,34 +497,34 @@ status_t SurfaceComposerClient::unfreeze(SurfaceID id) status_t SurfaceComposerClient::setFlags(SurfaceID id, uint32_t flags, uint32_t mask) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eVisibilityChanged; s->flags &= ~mask; s->flags |= (flags & mask); s->mask |= mask; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } status_t SurfaceComposerClient::setTransparentRegionHint( SurfaceID id, const Region& transparentRegion) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eTransparentRegionChanged; s->transparentRegion = transparentRegion; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eAlphaChanged; s->alpha = alpha; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } @@ -615,7 +533,7 @@ status_t SurfaceComposerClient::setMatrix( float dsdx, float dtdx, float dsdy, float dtdy ) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eMatrixChanged; layer_state_t::matrix22_t matrix; @@ -624,19 +542,56 @@ status_t SurfaceComposerClient::setMatrix( matrix.dsdy = dsdy; matrix.dtdy = dtdy; s->matrix = matrix; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) { - layer_state_t* s = _lockLayerState(id); + layer_state_t* s = lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eFreezeTintChanged; s->tint = tint; - _unlockLayerState(); + unlockLayerState(); return NO_ERROR; } +// ---------------------------------------------------------------------------- + +SurfaceClient::SurfaceClient(const sp<SurfaceComposerClient>& client) + : mStatus(NO_INIT), mControl(0) +{ + if (client != 0) { + sp<IBinder> conn = client->connection(); + init(conn); + } +} +SurfaceClient::SurfaceClient(const sp<IBinder>& conn) + : mStatus(NO_INIT), mControl(0) +{ + init(conn); +} +void SurfaceClient::init(const sp<IBinder>& conn) +{ + mComposerService = getComposerService(); + sp<ISurfaceComposerClient> sf(interface_cast<ISurfaceComposerClient>(conn)); + if (sf != 0) { + mConnection = conn; + mControlMemory = sf->getControlBlock(); + mControl = static_cast<SharedClient *>(mControlMemory->getBase()); + mStatus = NO_ERROR; + } +} +status_t SurfaceClient::initCheck() const { + return mStatus; +} +SharedClient* SurfaceClient::getSharedClient() const { + return mControl; +} +void SurfaceClient::signalServer() const { + mComposerService->signal(); +} + +// ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/surfaceflinger_client/tests/Android.mk b/libs/surfaceflinger_client/tests/Android.mk new file mode 100644 index 0000000..5053e7d --- /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 0000000..d3dfe04 --- /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 0000000..f409f48 --- /dev/null +++ b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp @@ -0,0 +1,279 @@ +/* + * 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); +void test0(SharedBufferServer& s, SharedBufferClient& c, size_t num, int* list); + +// ---------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ + SharedClient client; + SharedBufferServer s(&client, 0, 4, 0); + SharedBufferClient c(&client, 0, 4, 0); + + printf("basic test 0\n"); + int list0[4] = {0, 1, 2, 3}; + test0(s, c, 4, list0); + + printf("basic test 1\n"); + int list1[4] = {2, 1, 0, 3}; + test0(s, c, 4, list1); + + int b = c.dequeue(); + c.lock(b); + c.queue(b); + s.retireAndLock(); + + printf("basic test 2\n"); + int list2[4] = {1, 2, 3, 0}; + test0(s, c, 4, list2); + + + printf("resize test\n"); + class SetBufferCountIPC : public SharedBufferClient::SetBufferCountCallback { + SharedBufferServer& s; + virtual status_t operator()(int bufferCount) const { + return s.resize(bufferCount); + } + public: + SetBufferCountIPC(SharedBufferServer& s) : s(s) { } + } resize(s); + + c.setBufferCount(6, resize); + int list3[6] = {3, 2, 1, 4, 5, 0}; + test0(s, c, 6, list3); + + return 0; +} + +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"); +} + +// ---------------------------------------------------------------------------- + +void test0( + SharedBufferServer& s, + SharedBufferClient& c, + size_t num, + int* list) +{ + status_t err; + int b[num], u[num], r[num]; + + for (size_t i=0 ; i<num ; i++) { + b[i] = c.dequeue(); + assert(b[i]==list[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]==list[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]==list[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]==list[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] = b[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]==list[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]==u[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]==u[num-1]); + err = s.unlock(r[num-1]); + assert(err == 0); + log("RT", r+num-1, 1); + printf("\n"); +} diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 6ae7e74..d51664d 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/AssetManager.cpp b/libs/utils/AssetManager.cpp index 5a05e6a..60a0d82 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -824,7 +824,7 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, // TODO: look for previously-created shared memory slice? int method; - long uncompressedLen; + size_t uncompressedLen; //printf("USING Zip '%s'\n", pEntry->getFileName()); diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 7e0f881..a1401ad 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/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index 45f6c8b..16b219c 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -39,8 +39,8 @@ ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, } bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, - int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { ZipFileRO* zip = (ZipFileRO*)zipToken; ZipEntryRO entry = (ZipEntryRO)entryToken; return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 6c701dd..28dc512 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -29,6 +29,22 @@ #include <fcntl.h> #include <errno.h> #include <assert.h> +#include <unistd.h> + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif using namespace android; @@ -38,6 +54,7 @@ using namespace android; #define kEOCDSignature 0x06054b50 #define kEOCDLen 22 #define kEOCDNumEntries 8 // offset to #of entries in file +#define kEOCDSize 12 // size of the central directory #define kEOCDFileOffset 16 // offset to central directory #define kMaxCommentLen 65535 // longest possible in ushort @@ -90,9 +107,8 @@ int ZipFileRO::entryToIndex(const ZipEntryRO entry) const status_t ZipFileRO::open(const char* zipFileName) { int fd = -1; - off_t length; - assert(mFileMap == NULL); + assert(mDirectoryMap == NULL); /* * Open and map the specified file. @@ -103,172 +119,240 @@ status_t ZipFileRO::open(const char* zipFileName) return NAME_NOT_FOUND; } - length = lseek(fd, 0, SEEK_END); - if (length < 0) { + mFileLength = lseek(fd, 0, SEEK_END); + if (mFileLength < kEOCDLen) { close(fd); return UNKNOWN_ERROR; } - mFileMap = new FileMap(); - if (mFileMap == NULL) { - close(fd); - return NO_MEMORY; - } - if (!mFileMap->create(zipFileName, fd, 0, length, true)) { - LOGW("Unable to map '%s': %s\n", zipFileName, strerror(errno)); - close(fd); - return UNKNOWN_ERROR; + if (mFileName != NULL) { + free(mFileName); } + mFileName = strdup(zipFileName); mFd = fd; /* - * Got it mapped, verify it and create data structures for fast access. + * Find the Central Directory and store its size and number of entries. + */ + if (!mapCentralDirectory()) { + goto bail; + } + + /* + * Verify Central Directory and create data structures for fast access. */ if (!parseZipArchive()) { - mFileMap->release(); - mFileMap = NULL; - return UNKNOWN_ERROR; + goto bail; } return OK; + +bail: + free(mFileName); + mFileName = NULL; + close(fd); + return UNKNOWN_ERROR; } /* * Parse the Zip archive, verifying its contents and initializing internal * data structures. */ -bool ZipFileRO::parseZipArchive(void) +bool ZipFileRO::mapCentralDirectory(void) { -#define CHECK_OFFSET(_off) { \ - if ((unsigned int) (_off) >= maxOffset) { \ - LOGE("ERROR: bad offset %u (max %d): %s\n", \ - (unsigned int) (_off), maxOffset, #_off); \ - goto bail; \ - } \ - } - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - const unsigned char* ptr; - size_t length = mFileMap->getDataLength(); - bool result = false; - unsigned int i, numEntries, cdOffset; - unsigned int val; + size_t readAmount = kMaxEOCDSearch; + if (readAmount > (size_t) mFileLength) + readAmount = mFileLength; + + unsigned char* scanBuf = (unsigned char*) malloc(readAmount); + if (scanBuf == NULL) { + LOGW("couldn't allocate scanBuf: %s", strerror(errno)); + free(scanBuf); + return false; + } /* - * The first 4 bytes of the file will either be the local header - * signature for the first file (kLFHSignature) or, if the archive doesn't - * have any files in it, the end-of-central-directory signature - * (kEOCDSignature). + * Make sure this is a Zip archive. */ - val = get4LE(basePtr); - if (val == kEOCDSignature) { - LOGI("Found Zip archive, but it looks empty\n"); - goto bail; - } else if (val != kLFHSignature) { - LOGV("Not a Zip archive (found 0x%08x)\n", val); - goto bail; + if (lseek(mFd, 0, SEEK_SET) != 0) { + LOGW("seek to start failed: %s", strerror(errno)); + free(scanBuf); + return false; + } + + ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); + if (actual != (ssize_t) sizeof(int32_t)) { + LOGI("couldn't read first signature from zip archive: %s", strerror(errno)); + free(scanBuf); + return false; + } + + { + unsigned int header = get4LE(scanBuf); + if (header == kEOCDSignature) { + LOGI("Found Zip archive, but it looks empty\n"); + free(scanBuf); + return false; + } else if (header != kLFHSignature) { + LOGV("Not a Zip archive (found 0x%08x)\n", val); + free(scanBuf); + return false; + } } /* - * Find the EOCD. We'll find it immediately unless they have a file - * comment. + * Perform the traditional EOCD snipe hunt. + * + * We're searching for the End of Central Directory magic number, + * which appears at the start of the EOCD block. It's followed by + * 18 bytes of EOCD stuff and up to 64KB of archive comment. We + * need to read the last part of the file into a buffer, dig through + * it to find the magic number, parse some values out, and use those + * to determine the extent of the CD. + * + * We start by pulling in the last part of the file. */ - ptr = basePtr + length - kEOCDLen; + off_t searchStart = mFileLength - readAmount; - while (ptr >= basePtr) { - if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature) + if (lseek(mFd, searchStart, SEEK_SET) != searchStart) { + LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); + free(scanBuf); + return false; + } + actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); + if (actual != (ssize_t) readAmount) { + LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno)); + free(scanBuf); + return false; + } + + /* + * Scan backward for the EOCD magic. In an archive without a trailing + * comment, we'll find it on the first try. (We may want to consider + * doing an initial minimal read; if we don't find it, retry with a + * second read as above.) + */ + int i; + for (i = readAmount - kEOCDLen; i >= 0; i--) { + if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { + LOGV("+++ Found EOCD at buf+%d\n", i); break; - ptr--; + } } - if (ptr < basePtr) { - LOGI("Could not find end-of-central-directory in Zip\n"); - goto bail; + if (i < 0) { + LOGD("Zip: EOCD not found, %s is not zip\n", mFileName); + free(scanBuf); + return false; } + off_t eocdOffset = searchStart + i; + const unsigned char* eocdPtr = scanBuf + i; + + assert(eocdOffset < mFileLength); + /* - * There are two interesting items in the EOCD block: the number of - * entries in the file, and the file offset of the start of the - * central directory. - * - * (There's actually a count of the #of entries in this file, and for - * all files which comprise a spanned archive, but for our purposes - * we're only interested in the current file. Besides, we expect the - * two to be equivalent for our stuff.) + * Grab the CD offset and size, and the number of entries in the + * archive. Verify that they look reasonable. */ - numEntries = get2LE(ptr + kEOCDNumEntries); - cdOffset = get4LE(ptr + kEOCDFileOffset); + unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); + unsigned int dirSize = get4LE(eocdPtr + kEOCDSize); + unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset); + + if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { + LOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", + (long) dirOffset, dirSize, (long) eocdOffset); + free(scanBuf); + return false; + } + if (numEntries == 0) { + LOGW("empty archive?\n"); + free(scanBuf); + return false; + } - /* valid offsets are [0,EOCD] */ - unsigned int maxOffset; - maxOffset = (ptr - basePtr) +1; + LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", + numEntries, dirSize, dirOffset); - LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset); - if (numEntries == 0 || cdOffset >= length) { - LOGW("Invalid entries=%d offset=%d (len=%zd)\n", - numEntries, cdOffset, length); - goto bail; + mDirectoryMap = new FileMap(); + if (mDirectoryMap == NULL) { + LOGW("Unable to create directory map: %s", strerror(errno)); + free(scanBuf); + return false; } + if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { + LOGW("Unable to map '%s' (%zd to %zd): %s\n", mFileName, + dirOffset, dirOffset + dirSize, strerror(errno)); + free(scanBuf); + return false; + } + + mNumEntries = numEntries; + mDirectoryOffset = dirOffset; + + return true; +} + +bool ZipFileRO::parseZipArchive(void) +{ + bool result = false; + const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr(); + size_t cdLength = mDirectoryMap->getDataLength(); + int numEntries = mNumEntries; + /* * Create hash table. We have a minimum 75% load factor, possibly as * low as 50% after we round off to a power of 2. */ - mNumEntries = numEntries; - mHashTableSize = roundUpPower2(1 + ((numEntries * 4) / 3)); - mHashTable = (HashEntry*) calloc(1, sizeof(HashEntry) * mHashTableSize); + mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3); + mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry)); /* * Walk through the central directory, adding entries to the hash * table. */ - ptr = basePtr + cdOffset; - for (i = 0; i < numEntries; i++) { - unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; - const unsigned char* localHdr; - unsigned int hash; - + const unsigned char* ptr = cdPtr; + for (int i = 0; i < numEntries; i++) { if (get4LE(ptr) != kCDESignature) { LOGW("Missed a central dir sig (at %d)\n", i); goto bail; } - if (ptr + kCDELen > basePtr + length) { + if (ptr + kCDELen > cdPtr + cdLength) { LOGW("Ran off the end (at %d)\n", i); goto bail; } - localHdrOffset = get4LE(ptr + kCDELocalOffset); - CHECK_OFFSET(localHdrOffset); + long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); + if (localHdrOffset >= mDirectoryOffset) { + LOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); + goto bail; + } + + unsigned int fileNameLen, extraLen, commentLen, hash; + fileNameLen = get2LE(ptr + kCDENameLen); extraLen = get2LE(ptr + kCDEExtraLen); commentLen = get2LE(ptr + kCDECommentLen); - //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n", - // i, localHdrOffset, fileNameLen, extraLen, commentLen); - //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen); - /* add the CDE filename to the hash table */ hash = computeHash((const char*)ptr + kCDELen, fileNameLen); addToHash((const char*)ptr + kCDELen, fileNameLen, hash); - localHdr = basePtr + localHdrOffset; - if (get4LE(localHdr) != kLFHSignature) { - LOGW("Bad offset to local header: %d (at %d)\n", - localHdrOffset, i); + ptr += kCDELen + fileNameLen + extraLen + commentLen; + if ((size_t)(ptr - cdPtr) > cdLength) { + LOGW("bad CD advance (%d vs %zd) at entry %d\n", + (int) (ptr - cdPtr), cdLength, i); goto bail; } - - ptr += kCDELen + fileNameLen + extraLen + commentLen; - CHECK_OFFSET(ptr - basePtr); } - + LOGV("+++ zip good scan %d entries\n", numEntries); result = true; bail: return result; -#undef CHECK_OFFSET } - /* * Simple string hash function for non-null-terminated strings. */ @@ -315,7 +399,7 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const memcmp(mHashTable[ent].name, fileName, nameLen) == 0) { /* match */ - return (ZipEntryRO) (ent + kZipEntryAdj); + return (ZipEntryRO)(long)(ent + kZipEntryAdj); } ent = (ent + 1) & (mHashTableSize-1); @@ -354,20 +438,24 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const * Returns "false" if the offsets to the fields or the contents of the fields * appear to be bogus. */ -bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const { - int ent = entryToIndex(entry); + bool ret = false; + + const int ent = entryToIndex(entry); if (ent < 0) return false; + HashEntry hashEntry = mHashTable[ent]; + /* * Recover the start of the central directory entry from the filename - * pointer. + * pointer. The filename is the first entry past the fixed-size data, + * so we can just subtract back from that. */ - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - const unsigned char* ptr = (const unsigned char*) mHashTable[ent].name; - size_t zipLength = mFileMap->getDataLength(); + const unsigned char* ptr = (const unsigned char*) hashEntry.name; + off_t cdOffset = mDirectoryOffset; ptr -= kCDELen; @@ -380,48 +468,78 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, if (pCrc32 != NULL) *pCrc32 = get4LE(ptr + kCDECRC); + size_t compLen = get4LE(ptr + kCDECompLen); + if (pCompLen != NULL) + *pCompLen = compLen; + size_t uncompLen = get4LE(ptr + kCDEUncompLen); + if (pUncompLen != NULL) + *pUncompLen = uncompLen; + /* - * We need to make sure that the lengths are not so large that somebody - * trying to map the compressed or uncompressed data runs off the end - * of the mapped region. + * If requested, determine the offset of the start of the data. All we + * have is the offset to the Local File Header, which is variable size, + * so we have to read the contents of the struct to figure out where + * the actual data starts. + * + * We also need to make sure that the lengths are not so large that + * somebody trying to map the compressed or uncompressed data runs + * off the end of the mapped region. + * + * Note we don't verify compLen/uncompLen if they don't request the + * dataOffset, because dataOffset is expensive to determine. However, + * if they don't have the file offset, they're not likely to be doing + * anything with the contents. */ - unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset); - if (localHdrOffset + kLFHLen >= zipLength) { - LOGE("ERROR: bad local hdr offset in zip\n"); - return false; - } - const unsigned char* localHdr = basePtr + localHdrOffset; - off_t dataOffset = localHdrOffset + kLFHLen - + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen); - if ((unsigned long) dataOffset >= zipLength) { - LOGE("ERROR: bad data offset in zip\n"); - return false; - } + if (pOffset != NULL) { + long localHdrOffset = get4LE(ptr + kCDELocalOffset); + if (localHdrOffset + kLFHLen >= cdOffset) { + LOGE("ERROR: bad local hdr offset in zip\n"); + return false; + } - if (pCompLen != NULL) { - *pCompLen = get4LE(ptr + kCDECompLen); - if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) { - LOGE("ERROR: bad compressed length in zip\n"); + unsigned char lfhBuf[kLFHLen]; + if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); return false; } - } - if (pUncompLen != NULL) { - *pUncompLen = get4LE(ptr + kCDEUncompLen); - if (*pUncompLen < 0) { - LOGE("ERROR: negative uncompressed length in zip\n"); + ssize_t actual = + TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); + if (actual != sizeof(lfhBuf)) { + LOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } + + if (get4LE(lfhBuf) != kLFHSignature) { + LOGW("didn't find signature at start of lfh, offset=%ld\n", + localHdrOffset); return false; } + + off_t dataOffset = localHdrOffset + kLFHLen + + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); + if (dataOffset >= cdOffset) { + LOGW("bad data offset %ld in zip\n", (long) dataOffset); + return false; + } + + /* check lengths */ + if ((off_t)(dataOffset + compLen) > cdOffset) { + LOGW("bad compressed length in zip (%ld + %zd > %ld)\n", + (long) dataOffset, compLen, (long) cdOffset); + return false; + } + if (method == kCompressStored && - (size_t)(dataOffset + *pUncompLen) >= zipLength) + (off_t)(dataOffset + uncompLen) > cdOffset) { - LOGE("ERROR: bad uncompressed length in zip\n"); + LOGE("ERROR: bad uncompressed length in zip (%ld + %zd > %ld)\n", + (long) dataOffset, uncompLen, (long) cdOffset); return false; } - } - if (pOffset != NULL) { *pOffset = dataOffset; } + return true; } @@ -457,14 +575,14 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const */ FileMap* newMap; - long compLen; + size_t compLen; off_t offset; if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) return NULL; newMap = new FileMap(); - if (!newMap->create(mFileMap->getFileName(), mFd, offset, compLen, true)) { + if (!newMap->create(mFileName, mFd, offset, compLen, true)) { newMap->release(); return NULL; } @@ -480,19 +598,26 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const */ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const { - const int kSequentialMin = 32768; + const size_t kSequentialMin = 32768; bool result = false; int ent = entryToIndex(entry); if (ent < 0) return -1; - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); int method; - long uncompLen, compLen; + size_t uncompLen, compLen; off_t offset; + const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + FileMap* file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } + + ptr = (const unsigned char*) file->getDataPtr(); + /* * Experiment with madvise hint. When we want to uncompress a file, * we pull some stuff out of the central dir entry and then hit a @@ -507,17 +632,17 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const * pair of system calls are negated by a reduction in page faults. */ if (compLen > kSequentialMin) - mFileMap->advise(FileMap::SEQUENTIAL); + file->advise(FileMap::SEQUENTIAL); if (method == kCompressStored) { - memcpy(buffer, basePtr + offset, uncompLen); + memcpy(buffer, ptr, uncompLen); } else { - if (!inflateBuffer(buffer, basePtr + offset, uncompLen, compLen)) + if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) goto bail; } if (compLen > kSequentialMin) - mFileMap->advise(FileMap::NORMAL); + file->advise(FileMap::NORMAL); result = true; @@ -537,29 +662,34 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const if (ent < 0) return -1; - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); int method; - long uncompLen, compLen; + size_t uncompLen, compLen; off_t offset; + const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - if (method == kCompressStored) { - ssize_t actual; + const FileMap* file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } + + ptr = (const unsigned char*) file->getDataPtr(); - actual = write(fd, basePtr + offset, uncompLen); + if (method == kCompressStored) { + ssize_t actual = write(fd, ptr, uncompLen); if (actual < 0) { LOGE("Write failed: %s\n", strerror(errno)); goto bail; - } else if (actual != uncompLen) { - LOGE("Partial write during uncompress (%d of %ld)\n", - (int)actual, uncompLen); + } else if ((size_t) actual != uncompLen) { + LOGE("Partial write during uncompress (%zd of %zd)\n", + actual, uncompLen); goto bail; } else { LOGI("+++ successful write\n"); } } else { - if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen)) + if (!inflateBuffer(fd, ptr, uncompLen, compLen)) goto bail; } @@ -573,7 +703,7 @@ bail: * Uncompress "deflate" data from one buffer to another. */ /*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, - long uncompLen, long compLen) + size_t uncompLen, size_t compLen) { bool result = false; z_stream zstream; @@ -582,7 +712,7 @@ bail: /* * Initialize the zlib stream struct. */ - memset(&zstream, 0, sizeof(zstream)); + memset(&zstream, 0, sizeof(zstream)); zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; @@ -592,10 +722,10 @@ bail: zstream.avail_out = uncompLen; zstream.data_type = Z_UNKNOWN; - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { @@ -619,8 +749,8 @@ bail: } /* paranoia */ - if ((long) zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + if (zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %zd)\n", zstream.total_out, uncompLen); goto z_bail; } @@ -638,10 +768,10 @@ bail: * Uncompress "deflate" data from one buffer to an open file descriptor. */ /*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, - long uncompLen, long compLen) + size_t uncompLen, size_t compLen) { bool result = false; - const int kWriteBufSize = 32768; + const size_t kWriteBufSize = 32768; unsigned char writeBuf[kWriteBufSize]; z_stream zstream; int zerr; @@ -649,7 +779,7 @@ bail: /* * Initialize the zlib stream struct. */ - memset(&zstream, 0, sizeof(zstream)); + memset(&zstream, 0, sizeof(zstream)); zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; @@ -659,10 +789,10 @@ bail: zstream.avail_out = sizeof(writeBuf); zstream.data_type = Z_UNKNOWN; - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { @@ -708,8 +838,8 @@ bail: assert(zerr == Z_STREAM_END); /* other errors should've been caught */ /* paranoia */ - if ((long) zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + if (zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %zd)\n", zstream.total_out, uncompLen); goto z_bail; } diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index b6e0aae..7cb01d0 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -1525,8 +1525,13 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, } if (ggl_unlikely(attrib_list==0)) { - *num_config = 0; - return EGL_TRUE; + /* + * A NULL attrib_list should be treated as though it was an empty + * one (terminated with EGL_NONE) as defined in + * section 3.4.1 "Querying Configurations" in the EGL specification. + */ + static const EGLint dummy = EGL_NONE; + attrib_list = &dummy; } int numAttributes = 0; diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 89b3e1f..ba09d08 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; @@ -843,10 +843,12 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, EGLint patch_index = -1; GLint attr; size_t size = 0; - while ((attr=attrib_list[size]) != EGL_NONE) { - if (attr == EGL_CONFIG_ID) - patch_index = size; - size += 2; + if (attrib_list) { + while ((attr=attrib_list[size]) != EGL_NONE) { + if (attr == EGL_CONFIG_ID) + patch_index = size; + size += 2; + } } if (patch_index >= 0) { size += 2; // we need copy the sentinel as well diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h index 1fba209..c8f529a 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 ce522c8..ce40b5d 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; } /** |