summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camera/libcameraservice/Android.mk33
-rw-r--r--camera/libcameraservice/CameraHardwareStub.cpp45
-rw-r--r--camera/libcameraservice/CameraHardwareStub.h4
-rw-r--r--camera/libcameraservice/CameraService.cpp1779
-rw-r--r--camera/libcameraservice/CameraService.h254
-rw-r--r--camera/libcameraservice/FakeCamera.cpp25
-rw-r--r--camera/libcameraservice/FakeCamera.h2
-rw-r--r--camera/tests/CameraServiceTest/CameraServiceTest.cpp256
-rw-r--r--include/private/surfaceflinger/SharedBufferStack.h160
-rw-r--r--include/surfaceflinger/ISurface.h2
-rw-r--r--include/surfaceflinger/ISurfaceFlingerClient.h9
-rw-r--r--include/surfaceflinger/Surface.h64
-rw-r--r--include/ui/GraphicBufferAllocator.h2
-rw-r--r--include/ui/android_native_buffer.h9
-rw-r--r--include/ui/egl/android_natives.h56
-rw-r--r--include/utils/ResourceTypes.h31
-rw-r--r--include/utils/ZipFileCRO.h4
-rw-r--r--include/utils/ZipFileRO.h33
-rw-r--r--libs/audioflinger/AudioDumpInterface.cpp220
-rw-r--r--libs/audioflinger/AudioDumpInterface.h12
-rw-r--r--libs/audioflinger/AudioFlinger.cpp264
-rw-r--r--libs/audioflinger/AudioFlinger.h49
-rw-r--r--libs/audioflinger/AudioPolicyManagerBase.cpp21
-rw-r--r--libs/surfaceflinger/Android.mk1
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp15
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardware.h2
-rw-r--r--libs/surfaceflinger/Layer.cpp416
-rw-r--r--libs/surfaceflinger/Layer.h98
-rw-r--r--libs/surfaceflinger/LayerBase.cpp272
-rw-r--r--libs/surfaceflinger/LayerBase.h86
-rw-r--r--libs/surfaceflinger/LayerBlur.cpp53
-rw-r--r--libs/surfaceflinger/LayerBlur.h6
-rw-r--r--libs/surfaceflinger/LayerBuffer.cpp35
-rw-r--r--libs/surfaceflinger/LayerBuffer.h11
-rw-r--r--libs/surfaceflinger/LayerDim.cpp3
-rw-r--r--libs/surfaceflinger/LayerDim.h6
-rw-r--r--libs/surfaceflinger/SurfaceFlinger.cpp113
-rw-r--r--libs/surfaceflinger/SurfaceFlinger.h2
-rw-r--r--libs/surfaceflinger/TextureManager.cpp242
-rw-r--r--libs/surfaceflinger/TextureManager.h79
-rw-r--r--libs/surfaceflinger/Transform.cpp7
-rw-r--r--libs/surfaceflinger/Transform.h4
-rw-r--r--libs/surfaceflinger_client/ISurface.cpp17
-rw-r--r--libs/surfaceflinger_client/SharedBufferStack.cpp333
-rw-r--r--libs/surfaceflinger_client/Surface.cpp132
-rw-r--r--libs/surfaceflinger_client/tests/Android.mk1
-rw-r--r--libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk17
-rw-r--r--libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp271
-rw-r--r--libs/ui/GraphicBufferAllocator.cpp19
-rw-r--r--libs/utils/AssetManager.cpp2
-rw-r--r--libs/utils/ResourceTypes.cpp3
-rw-r--r--libs/utils/ZipFileCRO.cpp4
-rw-r--r--libs/utils/ZipFileRO.cpp462
-rw-r--r--opengl/libagl/egl.cpp9
-rw-r--r--opengl/libs/EGL/egl.cpp12
-rw-r--r--opengl/libs/egl_impl.h1
-rw-r--r--vpn/java/android/net/vpn/VpnManager.java3
57 files changed, 3454 insertions, 2617 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/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..a1a02e0 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -58,7 +58,7 @@ namespace android {
// When changing these values, the COMPILE_TIME_ASSERT at the end of this
// file need to be updated.
const unsigned int NUM_LAYERS_MAX = 31;
-const unsigned int NUM_BUFFER_MAX = 4;
+const unsigned int NUM_BUFFER_MAX = 16;
const unsigned int NUM_DISPLAY_MAX = 4;
// ----------------------------------------------------------------------------
@@ -69,7 +69,11 @@ class SharedClient;
// ----------------------------------------------------------------------------
-// should be 128 bytes (32 longs)
+// 4 * (11 + 7 + (1 + 2*NUM_RECT_MAX) * NUM_BUFFER_MAX) * NUM_LAYERS_MAX
+// 4 * (11 + 7 + (1 + 2*7)*16) * 31
+// 1032 * 31
+// = ~27 KiB (31992)
+
class SharedBufferStack
{
friend class SharedClient;
@@ -78,21 +82,31 @@ class SharedBufferStack
friend class SharedBufferServer;
public:
- struct FlatRegion { // 12 bytes
- static const unsigned int NUM_RECT_MAX = 1;
- uint32_t count;
- uint16_t rects[4*NUM_RECT_MAX];
- };
-
struct Statistics { // 4 longs
typedef int32_t usecs_t;
usecs_t totalTime;
usecs_t reserved[3];
};
+
+ struct SmallRect {
+ uint16_t l, t, r, b;
+ };
+
+ struct FlatRegion { // 52 bytes = 4 * (1 + 2*N)
+ static const unsigned int NUM_RECT_MAX = 6;
+ uint32_t count;
+ SmallRect rects[NUM_RECT_MAX];
+ };
+
+ struct BufferData {
+ FlatRegion dirtyRegion;
+ SmallRect crop;
+ };
SharedBufferStack();
void init(int32_t identity);
status_t setDirtyRegion(int buffer, const Region& reg);
+ status_t setCrop(int buffer, const Rect& reg);
Region getDirtyRegion(int buffer) const;
// these attributes are part of the conditions/updates
@@ -104,16 +118,18 @@ 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:
@@ -150,8 +166,9 @@ public:
protected:
SharedClient* const mSharedClient;
SharedBufferStack* const mSharedStack;
- const int mNumBuffers;
+ int mNumBuffers;
const int mIdentity;
+ int32_t computeTail() const;
friend struct Update;
friend struct QueueUpdate;
@@ -160,61 +177,22 @@ protected:
SharedBufferStack& stack;
inline ConditionBase(SharedBufferBase* sbc)
: stack(*sbc->mSharedStack) { }
+ virtual ~ConditionBase() { };
+ virtual bool operator()() const = 0;
+ virtual const char* name() const = 0;
};
+ status_t waitForCondition(const ConditionBase& condition);
struct UpdateBase {
SharedBufferStack& stack;
inline UpdateBase(SharedBufferBase* sbb)
: stack(*sbb->mSharedStack) { }
};
-
- template <typename T>
- status_t waitForCondition(T condition);
-
template <typename T>
status_t updateCondition(T update);
};
template <typename T>
-status_t SharedBufferBase::waitForCondition(T condition)
-{
- const SharedBufferStack& stack( *mSharedStack );
- SharedClient& client( *mSharedClient );
- const nsecs_t TIMEOUT = s2ns(1);
- Mutex::Autolock _l(client.lock);
- while ((condition()==false) &&
- (stack.identity == mIdentity) &&
- (stack.status == NO_ERROR))
- {
- status_t err = client.cv.waitRelative(client.lock, TIMEOUT);
-
- // handle errors and timeouts
- if (CC_UNLIKELY(err != NO_ERROR)) {
- if (err == TIMED_OUT) {
- if (condition()) {
- LOGE("waitForCondition(%s) timed out (identity=%d), "
- "but condition is true! We recovered but it "
- "shouldn't happen." , T::name(),
- stack.identity);
- break;
- } else {
- LOGW("waitForCondition(%s) timed out "
- "(identity=%d, status=%d). "
- "CPU may be pegged. trying again.", T::name(),
- stack.identity, stack.status);
- }
- } else {
- LOGE("waitForCondition(%s) error (%s) ",
- T::name(), strerror(-err));
- return err;
- }
- }
- }
- return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status;
-}
-
-
-template <typename T>
status_t SharedBufferBase::updateCondition(T update) {
SharedClient& client( *mSharedClient );
Mutex::Autolock _l(client.lock);
@@ -238,13 +216,13 @@ 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);
+ status_t setBufferCount(int bufferCount);
private:
friend struct Condition;
friend struct DequeueCondition;
friend struct LockCondition;
-
- int32_t computeTail() const;
struct QueueUpdate : public UpdateBase {
inline QueueUpdate(SharedBufferBase* sbb);
@@ -260,18 +238,20 @@ private:
struct DequeueCondition : public ConditionBase {
inline DequeueCondition(SharedBufferClient* sbc);
- inline bool operator()();
- static inline const char* name() { return "DequeueCondition"; }
+ inline bool operator()() const;
+ inline const char* name() const { return "DequeueCondition"; }
};
struct LockCondition : public ConditionBase {
int buf;
inline LockCondition(SharedBufferClient* sbc, int buf);
- inline bool operator()();
- static inline const char* name() { return "LockCondition"; }
+ inline bool operator()() const;
+ inline const char* name() const { return "LockCondition"; }
};
int32_t tail;
+ int32_t undoDequeueTail;
+ int32_t queued_head;
// statistics...
nsecs_t mDequeueTime[NUM_BUFFER_MAX];
};
@@ -290,13 +270,62 @@ public:
status_t reallocate();
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 = NUM_BUFFER_MAX) : mCapacity(c), mList(0) { }
+ status_t add(int value);
+ status_t remove(int value);
+
+ 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) );
+ }
+ };
+
+ BufferList mBufferList;
+
struct UnlockUpdate : public UpdateBase {
const int lockedBuffer;
inline UnlockUpdate(SharedBufferBase* sbb, int lockedBuffer);
@@ -318,8 +347,8 @@ private:
struct ReallocateCondition : public ConditionBase {
int buf;
inline ReallocateCondition(SharedBufferBase* sbb, int buf);
- inline bool operator()();
- static inline const char* name() { return "ReallocateCondition"; }
+ inline bool operator()() const;
+ inline const char* name() const { return "ReallocateCondition"; }
};
};
@@ -349,8 +378,7 @@ struct surface_flinger_cblk_t // 4KB max
// ---------------------------------------------------------------------------
-COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 4096)
-COMPILE_TIME_ASSERT(sizeof(SharedBufferStack) == 128)
+COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 32768)
COMPILE_TIME_ASSERT(sizeof(surface_flinger_cblk_t) <= 4096)
// ---------------------------------------------------------------------------
diff --git a/include/surfaceflinger/ISurface.h b/include/surfaceflinger/ISurface.h
index 472f759..9476686 100644
--- a/include/surfaceflinger/ISurface.h
+++ b/include/surfaceflinger/ISurface.h
@@ -47,12 +47,14 @@ 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;
+ virtual status_t setBufferCount(int bufferCount) = 0;
class BufferHeap {
public:
diff --git a/include/surfaceflinger/ISurfaceFlingerClient.h b/include/surfaceflinger/ISurfaceFlingerClient.h
index d257645..c96432f 100644
--- a/include/surfaceflinger/ISurfaceFlingerClient.h
+++ b/include/surfaceflinger/ISurfaceFlingerClient.h
@@ -59,6 +59,9 @@ public:
virtual sp<IMemoryHeap> getControlBlock() const = 0;
+ /*
+ * Requires ACCESS_SURFACE_FLINGER permission
+ */
virtual sp<ISurface> createSurface( surface_data_t* data,
int pid,
const String8& name,
@@ -68,8 +71,14 @@ public:
PixelFormat format,
uint32_t flags) = 0;
+ /*
+ * Requires ACCESS_SURFACE_FLINGER permission
+ */
virtual status_t destroySurface(SurfaceID sid) = 0;
+ /*
+ * Requires ACCESS_SURFACE_FLINGER permission
+ */
virtual status_t setState(int32_t count, const layer_state_t* states) = 0;
};
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 0279d84..973780f 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -30,6 +30,8 @@
#include <surfaceflinger/ISurface.h>
#include <surfaceflinger/ISurfaceFlingerClient.h>
+#define ANDROID_VIEW_SURFACE_JNI_ID "mNativeSurface"
+
namespace android {
// ---------------------------------------------------------------------------
@@ -163,38 +165,36 @@ public:
// setSwapRectangle() is intended to be used by GL ES clients
void setSwapRectangle(const Rect& r);
-private:
- // can't be copied
- Surface& operator = (Surface& rhs);
- Surface(const Surface& rhs);
-
- Surface(const sp<SurfaceControl>& control);
- void init();
- ~Surface();
-
- friend class SurfaceComposerClient;
- friend class SurfaceControl;
-
+private:
+ /*
+ * Android frameworks friends
+ * (eventually this should go away and be replaced by proper APIs)
+ */
// camera and camcorder need access to the ISurface binder interface for preview
friend class Camera;
friend class MediaRecorder;
- // mediaplayer needs access to ISurface for display
+ // MediaPlayer needs access to ISurface for display
friend class MediaPlayer;
friend class IOMX;
// this is just to be able to write some unit tests
friend class Test;
- sp<SurfaceComposerClient> getClient() const;
- sp<ISurface> getISurface() const;
+private:
+ friend class SurfaceComposerClient;
+ friend class SurfaceControl;
- status_t getBufferLocked(int index, int usage);
-
- status_t validate() const;
+ // can't be copied
+ Surface& operator = (Surface& rhs);
+ Surface(const Surface& rhs);
- inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; }
- inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; }
-
+ Surface(const sp<SurfaceControl>& control);
+ ~Surface();
+
+
+ /*
+ * android_native_window_t hooks
+ */
static int setSwapInterval(android_native_window_t* window, int interval);
static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer);
static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
@@ -208,15 +208,30 @@ private:
int query(int what, int* value);
int perform(int operation, va_list args);
- status_t dequeueBuffer(sp<GraphicBuffer>* buffer);
-
void dispatch_setUsage(va_list args);
int dispatch_connect(va_list args);
int dispatch_disconnect(va_list args);
+ int dispatch_crop(va_list args);
void setUsage(uint32_t reqUsage);
int connect(int api);
int disconnect(int api);
+ int crop(Rect const* rect);
+ int setBufferCount(int bufferCount);
+
+ /*
+ * private stuff...
+ */
+ void init();
+ status_t validate() const;
+ sp<SurfaceComposerClient> getClient() const;
+ sp<ISurface> getISurface() const;
+
+ inline const GraphicBufferMapper& getBufferMapper() const { return mBufferMapper; }
+ inline GraphicBufferMapper& getBufferMapper() { return mBufferMapper; }
+
+ status_t getBufferLocked(int index, int usage);
+ int getBufferIndex(const sp<GraphicBuffer>& buffer) const;
uint32_t getUsage() const;
int getConnectedApi() const;
@@ -235,6 +250,7 @@ private:
Rect mSwapRectangle;
uint32_t mUsage;
int mConnected;
+ Rect mNextBufferCrop;
// protected by mSurfaceLock. These are also used from lock/unlock
// but in that case, they must be called form the same thread.
@@ -245,7 +261,7 @@ private:
sp<GraphicBuffer> mLockedBuffer;
sp<GraphicBuffer> mPostedBuffer;
mutable Region mOldDirtyRegion;
- bool mNeedFullUpdate;
+ bool mReserved;
// query() must be called from dequeueBuffer() thread
uint32_t mWidth;
diff --git a/include/ui/GraphicBufferAllocator.h b/include/ui/GraphicBufferAllocator.h
index 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..49bfa2b 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -41,6 +41,14 @@ extern "C" {
struct android_native_buffer_t;
+typedef struct android_native_rect_t
+{
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+} android_native_rect_t;
+
// ---------------------------------------------------------------------------
typedef struct android_native_base_t
@@ -63,15 +71,16 @@ typedef struct android_native_base_t
/* attributes queriable with query() */
enum {
NATIVE_WINDOW_WIDTH = 0,
- NATIVE_WINDOW_HEIGHT = 1,
- NATIVE_WINDOW_FORMAT = 2,
+ NATIVE_WINDOW_HEIGHT,
+ NATIVE_WINDOW_FORMAT,
};
/* valid operations for the (*perform)() hook */
enum {
NATIVE_WINDOW_SET_USAGE = 0,
- NATIVE_WINDOW_CONNECT = 1,
- NATIVE_WINDOW_DISCONNECT = 2
+ NATIVE_WINDOW_CONNECT,
+ NATIVE_WINDOW_DISCONNECT,
+ NATIVE_WINDOW_SET_CROP,
};
/* parameter for NATIVE_WINDOW_[DIS]CONNECT */
@@ -89,6 +98,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 +143,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 +189,7 @@ typedef struct android_native_window_t
* NATIVE_WINDOW_SET_USAGE
* NATIVE_WINDOW_CONNECT
* NATIVE_WINDOW_DISCONNECT
+ * NATIVE_WINDOW_SET_CROP
*
*/
@@ -221,6 +240,24 @@ static inline int native_window_disconnect(
return window->perform(window, NATIVE_WINDOW_DISCONNECT, api);
}
+/*
+ * native_window_set_crop(..., crop) sets which region of the next queued
+ * buffers needs to be considered.
+ * A buffer's crop region is scaled to match the surface's size.
+ *
+ * The specified crop region applies to all buffers queued after it is called.
+ *
+ * if 'crop' is NULL, subsequently queued buffers won't be cropped.
+ *
+ * An error is returned if for instance the crop region is invalid,
+ * out of the buffer's bound or if the window is invalid.
+ */
+static inline int native_window_set_crop(
+ android_native_window_t* window,
+ android_native_rect_t const * crop)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_CROP, crop);
+}
// ---------------------------------------------------------------------------
@@ -263,6 +300,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/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/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/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..1fe997d 100644
--- a/libs/surfaceflinger/Layer.cpp
+++ b/libs/surfaceflinger/Layer.cpp
@@ -47,47 +47,49 @@ 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)
{
// 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.getBufferCount(),
+ getIdentity());
+
+ mBufferManager.setActiveBufferIndex( lcblk->getFrontBuffer() );
}
Layer::~Layer()
{
destroy();
// the actual buffers will be destroyed here
+ delete lcblk;
+}
+
+// called with SurfaceFlinger::mStateLock as soon as the layer is entered
+// in the purgatory list
+void Layer::onRemoved()
+{
+ // wake up the condition
+ lcblk->setStatus(NO_INIT);
}
void Layer::destroy()
{
- 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;
- }
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+ mBufferManager.destroy(dpy);
+
mSurface.clear();
+
+ Mutex::Autolock _l(mLock);
+ mWidth = mHeight = 0;
}
sp<LayerBaseClient::Surface> Layer::createSurface() const
@@ -131,24 +133,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 +153,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 +178,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,7 +205,31 @@ void Layer::onDraw(const Region& clip) const
}
return;
}
- drawWithOpenGL(clip, mTextures[index]);
+ drawWithOpenGL(clip, tex);
+}
+
+
+status_t Layer::setBufferCount(int bufferCount)
+{
+ // this 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;
+ }
+
+ status_t err;
+
+ // FIXME: resize() below is NOT thread-safe, we need to synchronize
+ // the users of lcblk in our process (ie: retire), and we assume the
+ // client is not mucking with the SharedStack, which is only enforced
+ // by construction, therefore we need to protect ourselves against
+ // buggy and malicious client (as always)
+
+ err = lcblk->resize(bufferCount);
+
+ return err;
}
sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
@@ -342,15 +266,7 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
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();
+ buffer = mBufferManager.detachBuffer(index);
}
const uint32_t effectiveUsage = getEffectiveUsage(usage);
@@ -378,10 +294,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;
+ mBufferManager.attachBuffer(index, buffer);
} else {
// oops we got killed while we were allocating the buffer
buffer.clear();
@@ -411,15 +324,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;
}
@@ -433,13 +339,10 @@ uint32_t Layer::doTransaction(uint32_t flags)
(front.requested_h != temp.requested_h)) {
// 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)",
+ "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),
- int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()),
- int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight()));
+ int(front.requested_w), int(front.requested_h));
// we're being resized and there is a freeze display request,
// acquire a freeze lock, so that the screen stays put
@@ -491,22 +394,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 +465,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 +497,151 @@ 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)
+ : mTextureManager(tm), mActiveBuffer(0), mFailover(false)
+{
+}
+
+size_t Layer::BufferManager::getBufferCount() const {
+ return NUM_BUFFERS;
+}
+
+// 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 {
+ return mFailover ? mFailoverTexture : mBufferData[mActiveBuffer].texture;
+}
+
+sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const {
+ Mutex::Autolock _l(mLock);
+ return mBufferData[mActiveBuffer].buffer;
+}
+
+sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index)
+{
+ sp<GraphicBuffer> buffer;
+ Mutex::Autolock _l(mLock);
+ buffer = mBufferData[index].buffer;
+ mBufferData[index].buffer = 0;
+ return buffer;
+}
+
+status_t Layer::BufferManager::attachBuffer(size_t index,
+ const sp<GraphicBuffer>& buffer)
+{
+ Mutex::Autolock _l(mLock);
+ mBufferData[index].buffer = buffer;
+ mBufferData[index].texture.dirty = true;
+ return NO_ERROR;
+}
+
+status_t Layer::BufferManager::destroyTexture(Texture* 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;
+}
+
+status_t Layer::BufferManager::destroy(EGLDisplay dpy)
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
+ destroyTexture(&mBufferData[i].texture, dpy);
+ mBufferData[i].buffer = 0;
+ }
+ destroyTexture(&mFailoverTexture, dpy);
+ return NO_ERROR;
+}
+
+status_t Layer::BufferManager::initEglImage(EGLDisplay dpy,
+ const sp<GraphicBuffer>& buffer)
+{
+ size_t index = mActiveBuffer;
+ Texture& 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;
+ for (size_t i=0 ; i<NUM_BUFFERS ; 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);
}
// ---------------------------------------------------------------------------
@@ -615,15 +661,21 @@ sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, int 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);
- }
+ buffer = owner->requestBuffer(index, usage);
}
return buffer;
}
+status_t Layer::SurfaceLayer::setBufferCount(int bufferCount)
+{
+ status_t err = DEAD_OBJECT;
+ sp<Layer> owner(getOwner());
+ if (owner != 0) {
+ err = owner->setBufferCount(bufferCount);
+ }
+ return err;
+}
+
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h
index 743afb4..80fbd6a 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);
@@ -71,26 +69,27 @@ public:
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);
+ status_t setBufferCount(int bufferCount);
void destroy();
class SurfaceLayer : public LayerBaseClient::Surface {
@@ -100,6 +99,7 @@ private:
~SurfaceLayer();
private:
virtual sp<GraphicBuffer> requestBuffer(int index, int usage);
+ virtual status_t setBufferCount(int bufferCount);
sp<Layer> getOwner() const {
return static_cast<Layer*>(Surface::getOwner().get());
}
@@ -109,22 +109,70 @@ 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;
+ Texture texture;
+ };
+ mutable Mutex mLock;
+ BufferData mBufferData[NUM_BUFFERS];
+ Texture mFailoverTexture;
+ TextureManager& mTextureManager;
+ ssize_t mActiveBuffer;
+ bool mFailover;
+ static status_t destroyTexture(Texture* tex, EGLDisplay dpy);
+
+ public:
+ BufferManager(TextureManager& tm);
+
+ size_t getBufferCount() const;
+
+ // detach/attach buffer from/to given index
+ sp<GraphicBuffer> detachBuffer(size_t index);
+ status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer);
+
+ // ----------------------------------------------
+ // 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;
+
+ mutable Mutex mLock;
+ uint32_t mWidth;
+ uint32_t mHeight;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp
index a8b735e..63b9520 100644
--- a/libs/surfaceflinger/LayerBase.cpp
+++ b/libs/surfaceflinger/LayerBase.cpp
@@ -32,20 +32,13 @@
#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),
@@ -54,7 +47,7 @@ LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
mOrientation(0),
mLeft(0), mTop(0),
mTransactionFlags(0),
- mPremultipliedAlpha(true), mDebug(false),
+ mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
mInvalidate(0)
{
const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
@@ -348,18 +341,6 @@ 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
@@ -374,7 +355,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 +399,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 +418,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 +449,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++;
@@ -502,200 +481,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;
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 +523,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 +552,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);
}
// ---------------------------------------------------------------------------
@@ -804,6 +621,11 @@ sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int index, int 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..53b848f 100644
--- a/libs/surfaceflinger/LayerBase.h
+++ b/libs/surfaceflinger/LayerBase.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().
@@ -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,35 +222,10 @@ 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) 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;
@@ -278,7 +234,7 @@ protected:
bool mTransformed;
bool mUseLinearFiltering;
int32_t mOrientation;
- GLfixed mVertices[4][2];
+ GLfloat mVertices[4][2];
Rect mTransformedBounds;
int mLeft;
int mTop;
@@ -313,14 +269,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 +279,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
{
@@ -357,6 +302,8 @@ public:
private:
virtual sp<GraphicBuffer> requestBuffer(int index, int 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..2d77876 100644
--- a/libs/surfaceflinger/LayerBlur.cpp
+++ b/libs/surfaceflinger/LayerBlur.cpp
@@ -33,11 +33,6 @@
namespace android {
// ---------------------------------------------------------------------------
-const uint32_t LayerBlur::typeInfo = LayerBaseClient::typeInfo | 8;
-const char* const LayerBlur::typeID = "LayerBlur";
-
-// ---------------------------------------------------------------------------
-
LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display,
const sp<Client>& client, int32_t i)
: LayerBaseClient(flinger, display, client, i), mCacheDirty(true),
@@ -206,8 +201,8 @@ void LayerBlur::onDraw(const Region& clip) const
const State& s = drawingState();
if (UNLIKELY(s.alpha < 0xFF)) {
- const GGLfixed alpha = (s.alpha << 16)/255;
- glColor4x(0, 0, 0, alpha);
+ const GLfloat alpha = s.alpha * (1.0f/255.0f);
+ glColor4f(0, 0, 0, alpha);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
@@ -225,38 +220,20 @@ void LayerBlur::onDraw(const Region& clip) const
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- if (UNLIKELY(transformed()
- || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) {
- // This is a very rare scenario.
- glMatrixMode(GL_TEXTURE);
- glLoadIdentity();
- glScalef(mWidthScale, mHeightScale, 1);
- glTranslatef(-x, mYOffset - y, 0);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glVertexPointer(2, GL_FIXED, 0, mVertices);
- glTexCoordPointer(2, GL_FIXED, 0, mVertices);
- while (it != end) {
- const Rect& r = *it++;
- const GLint sy = fbHeight - (r.top + r.height());
- glScissor(r.left, sy, r.width(), r.height());
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- }
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- } else {
- // NOTE: this is marginally faster with the software gl, because
- // glReadPixels() reads the fb bottom-to-top, however we'll
- // skip all the jaccobian computations.
- Rect r;
- GLint crop[4] = { 0, 0, w, h };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- y = fbHeight - (y + h);
- while (it != end) {
- const Rect& r = *it++;
- const GLint sy = fbHeight - (r.top + r.height());
- glScissor(r.left, sy, r.width(), r.height());
- glDrawTexiOES(x, y, 0, w, h);
- }
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glScalef(mWidthScale, mHeightScale, 1);
+ glTranslatef(-x, mYOffset - y, 0);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, mVertices);
+ glTexCoordPointer(2, GL_FLOAT, 0, mVertices);
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = fbHeight - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
}
diff --git a/libs/surfaceflinger/LayerBlur.h b/libs/surfaceflinger/LayerBlur.h
index 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..dfcc80f 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;
// ---------------------------------------------------------------------------
@@ -330,7 +328,7 @@ bool LayerBuffer::Source::transformed() const {
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
@@ -462,35 +460,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 +500,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;
@@ -593,7 +566,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 +583,6 @@ void LayerBuffer::BufferSource::clearTempBufferImage() const
glDeleteTextures(1, &mTexture.name);
Texture defaultTexture;
mTexture = defaultTexture;
- mTexture.name = mLayer.createTexture();
}
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h
index b176623..869c74f 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;
@@ -52,17 +53,13 @@ class LayerBuffer : public LayerBaseClient
};
public:
- static const uint32_t typeInfo;
- static const char* const typeID;
- virtual char const* getTypeID() const { return typeID; }
- virtual uint32_t getTypeInfo() const { return typeInfo; }
-
LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
const sp<Client>& client, int32_t i);
virtual ~LayerBuffer();
virtual void onFirstRef();
virtual bool needsBlending() const;
+ virtual const char* getTypeId() const { return "LayerBuffer"; }
virtual sp<LayerBaseClient::Surface> createSurface() const;
virtual status_t ditch();
@@ -143,9 +140,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/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 0722fda..62d829b 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -206,8 +206,8 @@ void SurfaceFlinger::init()
property_get("debug.sf.showbackground", value, "0");
mDebugBackground = atoi(value);
- LOGI_IF(mDebugRegion, "showupdates enabled");
- LOGI_IF(mDebugBackground, "showbackground enabled");
+ LOGI_IF(mDebugRegion, "showupdates enabled");
+ LOGI_IF(mDebugBackground, "showbackground enabled");
}
SurfaceFlinger::~SurfaceFlinger()
@@ -357,7 +357,6 @@ status_t SurfaceFlinger::readyToRun()
dcblk->ydpi = hw.getDpiY();
dcblk->fps = hw.getRefreshRate();
dcblk->density = hw.getDensity();
- asm volatile ("":::"memory");
// Initialize OpenGL|ES
glActiveTexture(GL_TEXTURE0);
@@ -1079,15 +1078,15 @@ status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer)
status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
{
- if (layer == 0)
- return BAD_VALUE;
ssize_t i = mCurrentState.layersSortedByZ.add(
layer, &LayerBase::compareCurrentStateZ);
- sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get());
- if (lbc != 0) {
- mLayerMap.add(lbc->serverIndex(), lbc);
- }
- return NO_ERROR;
+ return (i < 0) ? status_t(i) : status_t(NO_ERROR);
+}
+
+status_t SurfaceFlinger::addClientLayer_l(const sp<LayerBaseClient>& lbc)
+{
+ ssize_t serverIndex = lbc->serverIndex();
+ return mLayerMap.add(serverIndex, lbc);
}
status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase)
@@ -1095,10 +1094,9 @@ status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase)
ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase);
if (index >= 0) {
mLayersRemoved = true;
- sp<LayerBaseClient> layer =
- LayerBase::dynamicCast< LayerBaseClient* >(layerBase.get());
- if (layer != 0) {
- mLayerMap.removeItem(layer->serverIndex());
+ ssize_t serverIndex = layerBase->serverIndex();
+ if (serverIndex >= 0) {
+ mLayerMap.removeItem(serverIndex);
}
return NO_ERROR;
}
@@ -1298,7 +1296,7 @@ sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(
format = PIXEL_FORMAT_RGBA_8888;
break;
case PIXEL_FORMAT_OPAQUE:
- format = PIXEL_FORMAT_RGB_565;
+ format = PIXEL_FORMAT_RGBX_8888;
break;
}
@@ -1307,6 +1305,7 @@ sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(
if (LIKELY(err == NO_ERROR)) {
layer->initStates(w, h, flags);
addLayer_l(layer);
+ addClientLayer_l(layer);
} else {
LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err));
layer.clear();
@@ -1321,6 +1320,7 @@ sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked(
sp<LayerBlur> layer = new LayerBlur(this, display, client, id);
layer->initStates(w, h, flags);
addLayer_l(layer);
+ addClientLayer_l(layer);
return layer;
}
@@ -1331,6 +1331,7 @@ sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked(
sp<LayerDim> layer = new LayerDim(this, display, client, id);
layer->initStates(w, h, flags);
addLayer_l(layer);
+ addClientLayer_l(layer);
return layer;
}
@@ -1341,6 +1342,7 @@ sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked(
sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id);
layer->initStates(w, h, flags);
addLayer_l(layer);
+ addClientLayer_l(layer);
return layer;
}
@@ -1512,83 +1514,17 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
result.append(buffer);
}
- size_t s = mClientsMap.size();
- char name[64];
- for (size_t i=0 ; i<s ; i++) {
- sp<Client> client = mClientsMap.valueAt(i);
- sprintf(name, " Client (id=0x%08x)", client->cid);
- client->dump(name);
- }
const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
const size_t count = currentLayers.size();
for (size_t i=0 ; i<count ; i++) {
- /*** LayerBase ***/
- const sp<LayerBase>& layer = currentLayers[i];
- const Layer::State& s = layer->drawingState();
- snprintf(buffer, SIZE,
- "+ %s %p\n"
- " "
- "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), "
- "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, "
- "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
- layer->getTypeID(), layer.get(),
- s.z, layer->tx(), layer->ty(), s.w, s.h,
- layer->needsBlending(), layer->needsDithering(),
- layer->contentDirty,
- s.alpha, s.flags,
- s.transform[0][0], s.transform[0][1],
- s.transform[1][0], s.transform[1][1]);
- result.append(buffer);
- buffer[0] = 0;
- /*** LayerBaseClient ***/
- sp<LayerBaseClient> lbc =
- LayerBase::dynamicCast< LayerBaseClient* >(layer.get());
- if (lbc != 0) {
- sp<Client> client(lbc->client.promote());
- snprintf(buffer, SIZE,
- " name=%s\n", lbc->getName().string());
- result.append(buffer);
- snprintf(buffer, SIZE,
- " id=0x%08x, client=0x%08x, identity=%u\n",
- lbc->clientIndex(), client.get() ? client->cid : 0,
- lbc->getIdentity());
-
- result.append(buffer);
- buffer[0] = 0;
- }
- /*** Layer ***/
- sp<Layer> l = LayerBase::dynamicCast< Layer* >(layer.get());
- if (l != 0) {
- SharedBufferStack::Statistics stats = l->lcblk->getStats();
- result.append( l->lcblk->dump(" ") );
- sp<const GraphicBuffer> buf0(l->getBuffer(0));
- sp<const GraphicBuffer> buf1(l->getBuffer(1));
- uint32_t w0=0, h0=0, s0=0;
- uint32_t w1=0, h1=0, s1=0;
- if (buf0 != 0) {
- w0 = buf0->getWidth();
- h0 = buf0->getHeight();
- s0 = buf0->getStride();
- }
- if (buf1 != 0) {
- w1 = buf1->getWidth();
- h1 = buf1->getHeight();
- s1 = buf1->getStride();
- }
- snprintf(buffer, SIZE,
- " "
- "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u],"
- " freezeLock=%p, dq-q-time=%u us\n",
- l->pixelFormat(),
- w0, h0, s0, w1, h1, s1,
- l->getFreezeLock().get(), stats.totalTime);
- result.append(buffer);
- buffer[0] = 0;
- }
+ const sp<LayerBase>& layer(currentLayers[i]);
+ layer->dump(result, buffer, SIZE);
+ const Layer::State& s(layer->drawingState());
s.transparentRegion.dump(result, "transparentRegion");
layer->transparentRegionScreen.dump(result, "transparentRegionScreen");
layer->visibleRegionScreen.dump(result, "visibleRegionScreen");
}
+
mWormholeRegion.dump(result, "WormholeRegion");
const DisplayHardware& hw(graphicPlane(0).displayHardware());
snprintf(buffer, SIZE,
@@ -1601,16 +1537,19 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
" last transaction time : %f us\n",
mLastSwapBufferTime/1000.0, mLastTransactionTime/1000.0);
result.append(buffer);
+
if (inSwapBuffersDuration || !locked) {
snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n",
inSwapBuffersDuration/1000.0);
result.append(buffer);
}
+
if (inTransactionDuration || !locked) {
snprintf(buffer, SIZE, " transaction time: %f us\n",
inTransactionDuration/1000.0);
result.append(buffer);
}
+
snprintf(buffer, SIZE, " client count: %d\n", mClientsMap.size());
result.append(buffer);
const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
@@ -1773,10 +1712,6 @@ sp<LayerBaseClient> Client::getLayerUser(int32_t i) const {
return lbc;
}
-void Client::dump(const char* what)
-{
-}
-
// ---------------------------------------------------------------------------
#if 0
#pragma mark -
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h
index d75dc15..9c8de51 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/libs/surfaceflinger/SurfaceFlinger.h
@@ -73,7 +73,6 @@ public:
inline bool isValid(int32_t i) const;
sp<LayerBaseClient> getLayerUser(int32_t i) const;
- void dump(const char* what);
const Vector< wp<LayerBaseClient> >& getLayers() const {
return mLayers;
@@ -281,6 +280,7 @@ private:
void destroyConnection(ClientID cid);
sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const;
status_t addLayer_l(const sp<LayerBase>& layer);
+ status_t addClientLayer_l(const sp<LayerBaseClient>& lbc);
status_t removeLayer_l(const sp<LayerBase>& layer);
status_t purgatorizeLayer_l(const sp<LayerBase>& layer);
void free_resources_l();
diff --git a/libs/surfaceflinger/TextureManager.cpp b/libs/surfaceflinger/TextureManager.cpp
new file mode 100644
index 0000000..e5d5302
--- /dev/null
+++ b/libs/surfaceflinger/TextureManager.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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(Texture* 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->NPOTAdjust = false;
+ 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..90cb62b
--- /dev/null
+++ b/libs/surfaceflinger/TextureManager.h
@@ -0,0 +1,79 @@
+/*
+ * 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 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;
+};
+
+// ---------------------------------------------------------------------------
+
+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(Texture* 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/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp
index bb86199..c5d0c0e 100644
--- a/libs/surfaceflinger_client/ISurface.cpp
+++ b/libs/surfaceflinger_client/ISurface.cpp
@@ -83,6 +83,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;
@@ -146,6 +156,13 @@ status_t BnSurface::onTransact(
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/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index a17e8ac..dab8ed8 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -67,19 +67,47 @@ void SharedBufferStack::init(int32_t i)
identity = i;
}
+status_t SharedBufferStack::setCrop(int buffer, const Rect& crop)
+{
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return BAD_INDEX;
+
+ buffers[buffer].crop.l = uint16_t(crop.left);
+ buffers[buffer].crop.t = uint16_t(crop.top);
+ buffers[buffer].crop.r = uint16_t(crop.right);
+ buffers[buffer].crop.b = uint16_t(crop.bottom);
+ return NO_ERROR;
+}
+
status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty)
{
if (uint32_t(buffer) >= NUM_BUFFER_MAX)
return BAD_INDEX;
- // in the current implementation we only send a single rectangle
- const Rect bounds(dirty.getBounds());
- FlatRegion& reg(dirtyRegion[buffer]);
- reg.count = 1;
- reg.rects[0] = uint16_t(bounds.left);
- reg.rects[1] = uint16_t(bounds.top);
- reg.rects[2] = uint16_t(bounds.right);
- reg.rects[3] = uint16_t(bounds.bottom);
+ FlatRegion& reg(buffers[buffer].dirtyRegion);
+ if (dirty.isEmpty()) {
+ reg.count = 0;
+ return NO_ERROR;
+ }
+
+ size_t count;
+ Rect const* r = dirty.getArray(&count);
+ if (count > FlatRegion::NUM_RECT_MAX) {
+ const Rect bounds(dirty.getBounds());
+ reg.count = 1;
+ reg.rects[0].l = uint16_t(bounds.left);
+ reg.rects[0].t = uint16_t(bounds.top);
+ reg.rects[0].r = uint16_t(bounds.right);
+ reg.rects[0].b = uint16_t(bounds.bottom);
+ } else {
+ reg.count = count;
+ for (size_t i=0 ; i<count ; i++) {
+ reg.rects[i].l = uint16_t(r[i].left);
+ reg.rects[i].t = uint16_t(r[i].top);
+ reg.rects[i].r = uint16_t(r[i].right);
+ reg.rects[i].b = uint16_t(r[i].bottom);
+ }
+ }
return NO_ERROR;
}
@@ -89,8 +117,27 @@ Region SharedBufferStack::getDirtyRegion(int buffer) const
if (uint32_t(buffer) >= NUM_BUFFER_MAX)
return res;
- const FlatRegion& reg(dirtyRegion[buffer]);
- res.set(Rect(reg.rects[0], reg.rects[1], reg.rects[2], reg.rects[3]));
+ const FlatRegion& reg(buffers[buffer].dirtyRegion);
+ if (reg.count > FlatRegion::NUM_RECT_MAX)
+ return res;
+
+ if (reg.count == 1) {
+ const Rect r(
+ reg.rects[0].l,
+ reg.rects[0].t,
+ reg.rects[0].r,
+ reg.rects[0].b);
+ res.set(r);
+ } else {
+ for (size_t i=0 ; i<reg.count ; i++) {
+ const Rect r(
+ reg.rects[i].l,
+ reg.rects[i].t,
+ reg.rects[i].r,
+ reg.rects[i].b);
+ res.orSelf(r);
+ }
+ }
return res;
}
@@ -132,16 +179,70 @@ String8 SharedBufferBase::dump(char const* prefix) const
char buffer[SIZE];
String8 result;
SharedBufferStack& stack( *mSharedStack );
- int tail = (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers;
+ int tail = computeTail();
snprintf(buffer, SIZE,
"%s[ head=%2d, available=%2d, queued=%2d, tail=%2d ] "
- "reallocMask=%08x, inUse=%2d, identity=%d, status=%d\n",
+ "reallocMask=%08x, inUse=%2d, identity=%d, status=%d",
prefix, stack.head, stack.available, stack.queued, tail,
stack.reallocMask, stack.inUse, stack.identity, stack.status);
result.append(buffer);
+
+ snprintf(buffer, SIZE, " { ");
+ result.append(buffer);
+
+ for (int i=0 ; i<mNumBuffers ; i++) {
+ snprintf(buffer, SIZE, "%d ", stack.index[i]);
+ result.append(buffer);
+ }
+
+ snprintf(buffer, SIZE, " }\n");
+ result.append(buffer);
+
return result;
}
+int32_t SharedBufferBase::computeTail() const
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers;
+}
+
+status_t SharedBufferBase::waitForCondition(const ConditionBase& condition)
+{
+ const SharedBufferStack& stack( *mSharedStack );
+ SharedClient& client( *mSharedClient );
+ const nsecs_t TIMEOUT = s2ns(1);
+ const int identity = mIdentity;
+
+ Mutex::Autolock _l(client.lock);
+ while ((condition()==false) &&
+ (stack.identity == identity) &&
+ (stack.status == NO_ERROR))
+ {
+ status_t err = client.cv.waitRelative(client.lock, TIMEOUT);
+ // handle errors and timeouts
+ if (CC_UNLIKELY(err != NO_ERROR)) {
+ if (err == TIMED_OUT) {
+ if (condition()) {
+ LOGE("waitForCondition(%s) timed out (identity=%d), "
+ "but condition is true! We recovered but it "
+ "shouldn't happen." , condition.name(), stack.identity);
+ break;
+ } else {
+ LOGW("waitForCondition(%s) timed out "
+ "(identity=%d, status=%d). "
+ "CPU may be pegged. trying again.", condition.name(),
+ stack.identity, stack.status);
+ }
+ } else {
+ LOGE("waitForCondition(%s) error (%s) ",
+ condition.name(), strerror(-err));
+ return err;
+ }
+ }
+ }
+ return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status;
+}
// ============================================================================
// conditions and updates
// ============================================================================
@@ -149,24 +250,34 @@ String8 SharedBufferBase::dump(char const* prefix) const
SharedBufferClient::DequeueCondition::DequeueCondition(
SharedBufferClient* sbc) : ConditionBase(sbc) {
}
-bool SharedBufferClient::DequeueCondition::operator()() {
+bool SharedBufferClient::DequeueCondition::operator()() const {
return stack.available > 0;
}
SharedBufferClient::LockCondition::LockCondition(
SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) {
}
-bool SharedBufferClient::LockCondition::operator()() {
- return (buf != stack.head ||
+bool SharedBufferClient::LockCondition::operator()() const {
+ // NOTE: if stack.head is messed up, we could crash the client
+ // or cause some drawing artifacts. This is okay, as long as it is
+ // limited to the client.
+ return (buf != stack.index[stack.head] ||
(stack.queued > 0 && stack.inUse != buf));
}
SharedBufferServer::ReallocateCondition::ReallocateCondition(
SharedBufferBase* sbb, int buf) : ConditionBase(sbb), buf(buf) {
}
-bool SharedBufferServer::ReallocateCondition::operator()() {
+bool SharedBufferServer::ReallocateCondition::operator()() const {
+ int32_t head = stack.head;
+ if (uint32_t(head) >= NUM_BUFFER_MAX) {
+ // if stack.head is messed up, we cannot allow the server to
+ // crash (since stack.head is mapped on the client side)
+ stack.status = BAD_VALUE;
+ return false;
+ }
// TODO: we should also check that buf has been dequeued
- return (buf != stack.head);
+ return (buf != stack.index[head]);
}
// ----------------------------------------------------------------------------
@@ -206,11 +317,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) >= 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 +333,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,37 +361,19 @@ ssize_t SharedBufferServer::StatusUpdate::operator()() {
SharedBufferClient::SharedBufferClient(SharedClient* sharedClient,
int surface, int num, int32_t identity)
- : SharedBufferBase(sharedClient, surface, num, identity), tail(0)
-{
- tail = computeTail();
-}
-
-int32_t SharedBufferClient::computeTail() const
+ : SharedBufferBase(sharedClient, surface, num, identity),
+ tail(0), undoDequeueTail(0)
{
SharedBufferStack& stack( *mSharedStack );
- // we need to make sure we read available and head coherently,
- // w.r.t RetireUpdate.
- int32_t newTail;
- int32_t avail;
- int32_t head;
- do {
- avail = stack.available;
- head = stack.head;
- } while (stack.available != avail);
- newTail = head - avail + 1;
- if (newTail < 0) {
- newTail += mNumBuffers;
- } else if (newTail >= mNumBuffers) {
- newTail -= mNumBuffers;
- }
- return newTail;
+ tail = computeTail();
+ queued_head = stack.head;
}
ssize_t SharedBufferClient::dequeue()
{
SharedBufferStack& stack( *mSharedStack );
- if (stack.head == tail && stack.available == 2) {
+ if (stack.head == tail && stack.available == mNumBuffers) {
LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d",
tail, stack.head, stack.available, stack.queued);
}
@@ -301,9 +394,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 +407,19 @@ ssize_t SharedBufferClient::dequeue()
status_t SharedBufferClient::undoDequeue(int buf)
{
+ // TODO: we can only undo the previous dequeue, we should
+ // enforce that in the api
UndoDequeueUpdate update(this);
status_t err = updateCondition( update );
if (err == NO_ERROR) {
- tail = computeTail();
+ tail = undoDequeueTail;
}
return err;
}
status_t SharedBufferClient::lock(int buf)
{
+ SharedBufferStack& stack( *mSharedStack );
LockCondition condition(this, buf);
status_t err = waitForCondition(condition);
return err;
@@ -330,26 +427,47 @@ status_t SharedBufferClient::lock(int buf)
status_t SharedBufferClient::queue(int buf)
{
+ 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<<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(buf, reg);
+}
+
+status_t SharedBufferClient::setBufferCount(int bufferCount)
{
SharedBufferStack& stack( *mSharedStack );
- return stack.setDirtyRegion(buffer, reg);
+ if (uint32_t(bufferCount) >= NUM_BUFFER_MAX)
+ return BAD_VALUE;
+ mNumBuffers = bufferCount;
+ queued_head = (stack.head + stack.queued) % mNumBuffers;
+ return NO_ERROR;
}
// ----------------------------------------------------------------------------
@@ -363,20 +481,31 @@ SharedBufferServer::SharedBufferServer(SharedClient* sharedClient,
mSharedStack->available = num;
mSharedStack->queued = 0;
mSharedStack->reallocMask = 0;
- memset(mSharedStack->dirtyRegion, 0, sizeof(mSharedStack->dirtyRegion));
+ memset(mSharedStack->buffers, 0, sizeof(mSharedStack->buffers));
+ for (int i=0 ; i<num ; i++) {
+ mBufferList.add(i);
+ mSharedStack->index[i] = i;
+ }
}
ssize_t SharedBufferServer::retireAndLock()
{
RetireUpdate update(this, mNumBuffers);
ssize_t buf = updateCondition( update );
- LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", int(buf), dump("").string());
+ if (buf >= 0) {
+ if (uint32_t(buf) >= NUM_BUFFER_MAX)
+ return BAD_VALUE;
+ SharedBufferStack& stack( *mSharedStack );
+ buf = stack.index[buf];
+ LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s",
+ int(buf), dump("").string());
+ }
return buf;
}
-status_t SharedBufferServer::unlock(int buffer)
+status_t SharedBufferServer::unlock(int buf)
{
- UnlockUpdate update(this, buffer);
+ UnlockUpdate update(this, buf);
status_t err = updateCondition( update );
return err;
}
@@ -403,17 +532,70 @@ int32_t SharedBufferServer::getQueuedCount() const
return stack.queued;
}
-status_t SharedBufferServer::assertReallocate(int buffer)
+status_t SharedBufferServer::assertReallocate(int buf)
{
- ReallocateCondition condition(this, buffer);
+ // 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) >= NUM_BUFFER_MAX)
+ return BAD_VALUE;
+
+ // 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) >= 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 +604,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..afbeafb 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -17,8 +17,6 @@
#define LOG_TAG "Surface"
#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -28,8 +26,6 @@
#include <utils/CallStack.h>
#include <utils/Log.h>
-#include <pixelflinger/pixelflinger.h>
-
#include <binder/IPCThreadState.h>
#include <binder/IMemory.h>
@@ -55,6 +51,8 @@ static status_t copyBlt(
const sp<GraphicBuffer>& src,
const Region& reg)
{
+ // src and dst with, height and format must be identical. no verification
+ // is done here.
status_t err;
uint8_t const * src_bits = NULL;
err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits);
@@ -67,7 +65,6 @@ static status_t copyBlt(
Region::const_iterator head(reg.begin());
Region::const_iterator tail(reg.end());
if (head != tail && src_bits && dst_bits) {
- // NOTE: dst and src must be the same format
const size_t bpp = bytesPerPixel(src->format);
const size_t dbpr = dst->stride * bpp;
const size_t sbpr = src->stride * bpp;
@@ -354,7 +351,6 @@ void Surface::init()
// be default we request a hardware surface
mUsage = GRALLOC_USAGE_HW_RENDER;
mConnected = 0;
- mNeedFullUpdate = false;
}
Surface::~Surface()
@@ -467,18 +463,6 @@ int Surface::perform(android_native_window_t* window,
// ----------------------------------------------------------------------------
-status_t Surface::dequeueBuffer(sp<GraphicBuffer>* buffer) {
- android_native_buffer_t* out;
- status_t err = dequeueBuffer(&out);
- if (err == NO_ERROR) {
- *buffer = GraphicBuffer::getSelf(out);
- }
- return err;
-}
-
-// ----------------------------------------------------------------------------
-
-
int Surface::dequeueBuffer(android_native_buffer_t** buffer)
{
sp<SurfaceComposerClient> client(getClient());
@@ -495,9 +479,12 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer)
// below we make sure we AT LEAST have the usage flags we want
const uint32_t usage(getUsage());
const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]);
+
+ // Always call needNewBuffer(), since it clears the needed buffers flags
+ bool needNewBuffer = mSharedBufferClient->needNewBuffer(bufIdx);
if (backBuffer == 0 ||
((uint32_t(backBuffer->usage) & usage) != usage) ||
- mSharedBufferClient->needNewBuffer(bufIdx))
+ needNewBuffer)
{
err = getBufferLocked(bufIdx, usage);
LOGE_IF(err, "getBufferLocked(%ld, %08x) failed (%s)",
@@ -531,7 +518,7 @@ int Surface::lockBuffer(android_native_buffer_t* buffer)
if (err != NO_ERROR)
return err;
- int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex();
+ int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
err = mSharedBufferClient->lock(bufIdx);
LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err));
return err;
@@ -548,7 +535,8 @@ int Surface::queueBuffer(android_native_buffer_t* buffer)
mDirtyRegion.set(mSwapRectangle);
}
- int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex();
+ int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+ mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop);
mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion);
err = mSharedBufferClient->queue(bufIdx);
LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err));
@@ -578,6 +566,10 @@ int Surface::query(int what, int* value)
int Surface::perform(int operation, va_list args)
{
+ status_t err = validate();
+ if (err != NO_ERROR)
+ return err;
+
int res = NO_ERROR;
switch (operation) {
case NATIVE_WINDOW_SET_USAGE:
@@ -589,6 +581,9 @@ int Surface::perform(int operation, va_list args)
case NATIVE_WINDOW_DISCONNECT:
res = dispatch_disconnect( args );
break;
+ case NATIVE_WINDOW_SET_CROP:
+ res = dispatch_crop( args );
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -608,6 +603,10 @@ int Surface::dispatch_disconnect(va_list args) {
int api = va_arg(args, int);
return disconnect( api );
}
+int Surface::dispatch_crop(va_list args) {
+ android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
+ return crop( reinterpret_cast<Rect const*>(rect) );
+}
void Surface::setUsage(uint32_t reqUsage)
@@ -666,6 +665,35 @@ int Surface::getConnectedApi() const
return mConnected;
}
+int Surface::crop(Rect const* rect)
+{
+ Mutex::Autolock _l(mSurfaceLock);
+ // TODO: validate rect size
+ mNextBufferCrop = *rect;
+ return NO_ERROR;
+}
+
+int Surface::setBufferCount(int bufferCount)
+{
+ sp<ISurface> s(mSurface);
+ if (s == 0) return NO_INIT;
+
+ // FIXME: this needs to be synchronized dequeue/queue
+
+ status_t err = s->setBufferCount(bufferCount);
+ LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s",
+ bufferCount, strerror(-err));
+ if (err == NO_ERROR) {
+ err = mSharedBufferClient->getStatus();
+ LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err);
+ if (!err) {
+ // update our local copy of the buffer count
+ mSharedBufferClient->setBufferCount(bufferCount);
+ }
+ }
+ return err;
+}
+
// ----------------------------------------------------------------------------
@@ -703,45 +731,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 +807,7 @@ status_t Surface::unlockAndPost()
err = queueBuffer(mLockedBuffer.get());
LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)",
- mLockedBuffer->getIndex(), strerror(-err));
+ getBufferIndex(mLockedBuffer), strerror(-err));
mPostedBuffer = mLockedBuffer;
mLockedBuffer = 0;
@@ -789,6 +819,11 @@ void Surface::setSwapRectangle(const Rect& r) {
mSwapRectangle = r;
}
+int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const
+{
+ return buffer->getIndex();
+}
+
status_t Surface::getBufferLocked(int index, int usage)
{
sp<ISurface> s(mSurface);
@@ -820,7 +855,6 @@ status_t Surface::getBufferLocked(int index, int usage)
if (err == NO_ERROR) {
currentBuffer = buffer;
currentBuffer->setIndex(index);
- mNeedFullUpdate = true;
}
} else {
err = err<0 ? err : NO_MEMORY;
diff --git a/libs/surfaceflinger_client/tests/Android.mk b/libs/surfaceflinger_client/tests/Android.mk
new file mode 100644
index 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..f490a65
--- /dev/null
+++ b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp
@@ -0,0 +1,271 @@
+/*
+ * 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");
+ s.resize(6);
+ c.setBufferCount(6);
+ 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;
}
/**