Merge "Squashed commit of the following:"
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index d2d3bb8..e705c6f 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -31,20 +31,27 @@
namespace android {
// ----------------------------------------------------------------------------
+class SurfaceTextureClient;
+
class ISurfaceTexture : public IInterface
{
public:
DECLARE_META_INTERFACE(SurfaceTexture);
- enum { BUFFER_NEEDS_REALLOCATION = 1 };
+protected:
+ friend class SurfaceTextureClient;
+
+ enum {
+ BUFFER_NEEDS_REALLOCATION = 0x1,
+ RELEASE_ALL_BUFFERS = 0x2,
+ };
// requestBuffer requests a new buffer for the given index. The server (i.e.
// the ISurfaceTexture implementation) assigns the newly created buffer to
// the given slot index, and the client is expected to mirror the
// slot->buffer mapping so that it's not necessary to transfer a
// GraphicBuffer for every dequeue operation.
- virtual sp<GraphicBuffer> requestBuffer(int slot, uint32_t w, uint32_t h,
- uint32_t format, uint32_t usage) = 0;
+ virtual sp<GraphicBuffer> requestBuffer(int slot) = 0;
// setBufferCount sets the number of buffer slots available. Calling this
// will also cause all buffer slots to be emptied. The caller should empty
@@ -60,7 +67,8 @@
// in the contents of its associated buffer contents and call queueBuffer.
// If dequeueBuffer return BUFFER_NEEDS_REALLOCATION, the client is
// expected to call requestBuffer immediately.
- virtual status_t dequeueBuffer(int *slot) = 0;
+ virtual status_t dequeueBuffer(int *slot, uint32_t w, uint32_t h,
+ uint32_t format, uint32_t usage) = 0;
// queueBuffer indicates that the client has finished filling in the
// contents of the buffer associated with slot and transfers ownership of
@@ -85,6 +93,17 @@
// Holding this binder reference prevents SurfaceFlinger from freeing the
// buffers before the client is done with them.
virtual sp<IBinder> getAllocator() = 0;
+
+ // query retrieves some information for this surface
+ // 'what' tokens allowed are that of android_natives.h
+ virtual int query(int what, int* value) = 0;
+
+ // setSynchronousMode set whether dequeueBuffer is synchronous or
+ // asynchronous. In synchronous mode, dequeueBuffer blocks until
+ // a buffer is available, the currently bound buffer can be dequeued and
+ // queued buffers will be retired in order.
+ // The default mode is asynchronous.
+ virtual status_t setSynchronousMode(bool enabled) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 96828c6..43b2fa9 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -34,14 +34,23 @@
// ----------------------------------------------------------------------------
class IGraphicBufferAlloc;
+class String8;
class SurfaceTexture : public BnSurfaceTexture {
public:
enum { MIN_UNDEQUEUED_BUFFERS = 2 };
- enum { MIN_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1 };
+ enum {
+ MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1,
+ MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS
+ };
enum { NUM_BUFFER_SLOTS = 32 };
struct FrameAvailableListener : public virtual RefBase {
+ // onFrameAvailable() is called from queueBuffer() is the FIFO is
+ // empty. You can use SurfaceTexture::getQueuedCount() to
+ // figure out if there are more frames waiting.
+ // This is called without any lock held can be called concurrently by
+ // multiple threads.
virtual void onFrameAvailable() = 0;
};
@@ -56,15 +65,15 @@
// SurfaceTexture object (i.e. they are not owned by the client).
virtual status_t setBufferCount(int bufferCount);
- virtual sp<GraphicBuffer> requestBuffer(int buf, uint32_t w, uint32_t h,
- uint32_t format, uint32_t usage);
+ virtual sp<GraphicBuffer> requestBuffer(int buf);
// dequeueBuffer gets the next buffer slot index for the client to use. If a
// buffer slot is available then that slot index is written to the location
// pointed to by the buf argument and a status of OK is returned. If no
// slot is available then a status of -EBUSY is returned and buf is
// unmodified.
- virtual status_t dequeueBuffer(int *buf);
+ virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h,
+ uint32_t format, uint32_t usage);
// queueBuffer returns a filled buffer to the SurfaceTexture. In addition, a
// timestamp must be provided for the buffer. The timestamp is in
@@ -76,6 +85,15 @@
virtual status_t setCrop(const Rect& reg);
virtual status_t setTransform(uint32_t transform);
+ virtual int query(int what, int* value);
+
+ // setSynchronousMode set whether dequeueBuffer is synchronous or
+ // asynchronous. In synchronous mode, dequeueBuffer blocks until
+ // a buffer is available, the currently bound buffer can be dequeued and
+ // queued buffers will be retired in order.
+ // The default mode is asynchronous.
+ virtual status_t setSynchronousMode(bool enabled);
+
// updateTexImage sets the image contents of the target texture to that of
// the most recently queued buffer.
//
@@ -83,6 +101,16 @@
// target texture belongs is bound to the calling thread.
status_t updateTexImage();
+ // getqueuedCount returns the number of queued frames waiting in the
+ // FIFO. In asynchronous mode, this always returns 0 or 1 since
+ // frames are not accumulating in the FIFO.
+ size_t getQueuedCount() const;
+
+ // setBufferCountServer set the buffer count. If the client has requested
+ // a buffer count using setBufferCount, the server-buffer count will
+ // take effect once the client sets the count back to zero.
+ status_t setBufferCountServer(int bufferCount);
+
// getTransformMatrix retrieves the 4x4 texture coordinate transform matrix
// associated with the texture image set by the most recent call to
// updateTexImage.
@@ -140,6 +168,10 @@
// getCurrentTransform returns the transform of the current buffer
uint32_t getCurrentTransform() const;
+ // dump our state in a String
+ void dump(String8& result) const;
+ void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
+
protected:
// freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
@@ -154,9 +186,21 @@
EGLImageKHR createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer);
+ status_t setBufferCountServerLocked(int bufferCount);
+
enum { INVALID_BUFFER_SLOT = -1 };
struct BufferSlot {
+
+ BufferSlot()
+ : mEglImage(EGL_NO_IMAGE_KHR),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mBufferState(BufferSlot::FREE),
+ mRequestBufferCalled(false),
+ mLastQueuedTransform(0),
+ mLastQueuedTimestamp(0) {
+ }
+
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
// if no buffer has been allocated.
sp<GraphicBuffer> mGraphicBuffer;
@@ -167,11 +211,32 @@
// mEglDisplay is the EGLDisplay used to create mEglImage.
EGLDisplay mEglDisplay;
- // mOwnedByClient indicates whether the slot is currently accessible to a
+ // mBufferState indicates whether the slot is currently accessible to a
// client and should not be used by the SurfaceTexture object. It gets
// set to true when dequeueBuffer returns the slot and is reset to false
// when the client calls either queueBuffer or cancelBuffer on the slot.
- bool mOwnedByClient;
+ enum { DEQUEUED=-2, FREE=-1, QUEUED=0 };
+ int8_t mBufferState;
+
+
+ // mRequestBufferCalled is used for validating that the client did
+ // call requestBuffer() when told to do so. Technically this is not
+ // needed but useful for debugging and catching client bugs.
+ bool mRequestBufferCalled;
+
+ // mLastQueuedCrop is the crop rectangle for the buffer that was most
+ // recently queued. This gets set to mNextCrop each time queueBuffer gets
+ // called.
+ Rect mLastQueuedCrop;
+
+ // mLastQueuedTransform is the transform identifier for the buffer that was
+ // most recently queued. This gets set to mNextTransform each time
+ // queueBuffer gets called.
+ uint32_t mLastQueuedTransform;
+
+ // mLastQueuedTimestamp is the timestamp for the buffer that was most
+ // recently queued. This gets set by queueBuffer.
+ int64_t mLastQueuedTimestamp;
};
// mSlots is the array of buffer slots that must be mirrored on the client
@@ -193,16 +258,19 @@
// in requestBuffers() if a format of zero is specified.
uint32_t mPixelFormat;
- // mUseDefaultSize indicates whether or not the default size should be used
- // that is, if the last requestBuffer has been called with both width
- // and height null.
- bool mUseDefaultSize;
-
// mBufferCount is the number of buffer slots that the client and server
- // must maintain. It defaults to MIN_BUFFER_SLOTS and can be changed by
- // calling setBufferCount.
+ // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed
+ // by calling setBufferCount or setBufferCountServer
int mBufferCount;
+ // mRequestedBufferCount is the number of buffer slots requested by the
+ // client. The default is zero, which means the client doesn't care how
+ // many buffers there is.
+ int mClientBufferCount;
+
+ // mServerBufferCount buffer count requested by the server-side
+ int mServerBufferCount;
+
// mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
// indicating that no buffer slot is currently bound to the texture. Note,
@@ -233,25 +301,6 @@
// gets set to mLastQueuedTimestamp each time updateTexImage is called.
int64_t mCurrentTimestamp;
- // mLastQueued is the buffer slot index of the most recently enqueued buffer.
- // At construction time it is initialized to INVALID_BUFFER_SLOT, and is
- // updated each time queueBuffer is called.
- int mLastQueued;
-
- // mLastQueuedCrop is the crop rectangle for the buffer that was most
- // recently queued. This gets set to mNextCrop each time queueBuffer gets
- // called.
- Rect mLastQueuedCrop;
-
- // mLastQueuedTransform is the transform identifier for the buffer that was
- // most recently queued. This gets set to mNextTransform each time
- // queueBuffer gets called.
- uint32_t mLastQueuedTransform;
-
- // mLastQueuedTimestamp is the timestamp for the buffer that was most
- // recently queued. This gets set by queueBuffer.
- int64_t mLastQueuedTimestamp;
-
// mNextCrop is the crop rectangle that will be used for the next buffer
// that gets queued. It is set by calling setCrop.
Rect mNextCrop;
@@ -274,6 +323,16 @@
// queueBuffer.
sp<FrameAvailableListener> mFrameAvailableListener;
+ // mSynchronousMode whether we're in synchronous mode or not
+ bool mSynchronousMode;
+
+ // mDequeueCondition condition used for dequeueBuffer in synchronous mode
+ mutable Condition mDequeueCondition;
+
+ // mQueue is a FIFO of queued buffers used in synchronous mode
+ typedef Vector<int> Fifo;
+ Fifo mQueue;
+
// mMutex is the mutex used to prevent concurrent access to the member
// variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed.
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index c77bc4c..e7c6e24 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -84,7 +84,6 @@
int getConnectedApi() const;
enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS };
- enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS };
enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h
index b1f5045..1bb04a7 100644
--- a/include/utils/BackupHelpers.h
+++ b/include/utils/BackupHelpers.h
@@ -70,6 +70,14 @@
~BackupDataWriter();
status_t WriteEntityHeader(const String8& key, size_t dataSize);
+
+ /* Note: WriteEntityData will write arbitrary data into the file without
+ * validation or a previously-supplied header. The full backup implementation
+ * uses it this way to generate a controlled binary stream that is not
+ * entity-structured. If the implementation here is changed, either this
+ * use case must remain valid, or the full backup implementation should be
+ * adjusted to use some other appropriate mechanism.
+ */
status_t WriteEntityData(const void* data, size_t size);
void SetKeyPrefix(const String8& keyPrefix);
@@ -103,7 +111,7 @@
bool HasEntities();
status_t ReadEntityHeader(String8* key, size_t* dataSize);
- status_t SkipEntityData(); // must be called with the pointer at the begining of the data.
+ status_t SkipEntityData(); // must be called with the pointer at the beginning of the data.
ssize_t ReadEntityData(void* data, size_t size);
private:
@@ -126,6 +134,9 @@
int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
char const* const* files, char const* const *keys, int fileCount);
+int write_tarfile(const String8& packageName, const String8& domain,
+ const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream);
+
class RestoreHelperBase
{
public:
diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp
index bc14ad5..16e3780 100644
--- a/libs/gui/ISurfaceTexture.cpp
+++ b/libs/gui/ISurfaceTexture.cpp
@@ -39,6 +39,8 @@
SET_CROP,
SET_TRANSFORM,
GET_ALLOCATOR,
+ QUERY,
+ SET_SYNCHRONOUS_MODE,
};
@@ -50,15 +52,10 @@
{
}
- virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
- uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
+ virtual sp<GraphicBuffer> requestBuffer(int bufferIdx) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
- data.writeInt32(w);
- data.writeInt32(h);
- data.writeInt32(format);
- data.writeInt32(usage);
remote()->transact(REQUEST_BUFFER, data, &reply);
sp<GraphicBuffer> buffer;
bool nonNull = reply.readInt32();
@@ -79,9 +76,14 @@
return err;
}
- virtual status_t dequeueBuffer(int *buf) {
+ virtual status_t dequeueBuffer(int *buf, uint32_t w, uint32_t h,
+ uint32_t format, uint32_t usage) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInt32(w);
+ data.writeInt32(h);
+ data.writeInt32(format);
+ data.writeInt32(usage);
remote()->transact(DEQUEUE_BUFFER, data, &reply);
*buf = reply.readInt32();
int result = reply.readInt32();
@@ -132,6 +134,27 @@
remote()->transact(GET_ALLOCATOR, data, &reply);
return reply.readStrongBinder();
}
+
+ virtual int query(int what, int* value) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInt32(what);
+ remote()->transact(QUERY, data, &reply);
+ value[0] = reply.readInt32();
+ status_t result = reply.readInt32();
+ return result;
+ }
+
+ virtual status_t setSynchronousMode(bool enabled) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInt32(enabled);
+ remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply);
+ status_t result = reply.readInt32();
+ return result;
+ }
+
+
};
IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture");
@@ -145,12 +168,7 @@
case REQUEST_BUFFER: {
CHECK_INTERFACE(ISurfaceTexture, data, reply);
int bufferIdx = data.readInt32();
- uint32_t w = data.readInt32();
- uint32_t h = data.readInt32();
- uint32_t format = data.readInt32();
- uint32_t usage = data.readInt32();
- sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format,
- usage));
+ sp<GraphicBuffer> buffer(requestBuffer(bufferIdx));
reply->writeInt32(buffer != 0);
if (buffer != 0) {
reply->write(*buffer);
@@ -166,8 +184,12 @@
} break;
case DEQUEUE_BUFFER: {
CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ uint32_t w = data.readInt32();
+ uint32_t h = data.readInt32();
+ uint32_t format = data.readInt32();
+ uint32_t usage = data.readInt32();
int buf;
- int result = dequeueBuffer(&buf);
+ int result = dequeueBuffer(&buf, w, h, format, usage);
reply->writeInt32(buf);
reply->writeInt32(result);
return NO_ERROR;
@@ -210,6 +232,22 @@
reply->writeStrongBinder(result);
return NO_ERROR;
} break;
+ case QUERY: {
+ CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ int value;
+ int what = data.readInt32();
+ int res = query(what, &value);
+ reply->writeInt32(value);
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
+ case SET_SYNCHRONOUS_MODE: {
+ CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ bool enabled = data.readInt32();
+ status_t res = setSynchronousMode(enabled);
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 2619629..d7c449c 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -34,6 +34,7 @@
#include <surfaceflinger/IGraphicBufferAlloc.h>
#include <utils/Log.h>
+#include <utils/String8.h>
namespace android {
@@ -81,23 +82,17 @@
mDefaultWidth(1),
mDefaultHeight(1),
mPixelFormat(PIXEL_FORMAT_RGBA_8888),
- mUseDefaultSize(true),
- mBufferCount(MIN_BUFFER_SLOTS),
+ mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+ mClientBufferCount(0),
+ mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
mCurrentTexture(INVALID_BUFFER_SLOT),
mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES),
mCurrentTransform(0),
mCurrentTimestamp(0),
- mLastQueued(INVALID_BUFFER_SLOT),
- mLastQueuedTransform(0),
- mLastQueuedTimestamp(0),
mNextTransform(0),
- mTexName(tex) {
+ mTexName(tex),
+ mSynchronousMode(false) {
LOGV("SurfaceTexture::SurfaceTexture");
- for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
- mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
- mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
- mSlots[i].mOwnedByClient = false;
- }
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
mNextCrop.makeInvalid();
@@ -108,18 +103,80 @@
freeAllBuffers();
}
+status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) {
+ if (bufferCount > NUM_BUFFER_SLOTS)
+ return BAD_VALUE;
+
+ // special-case, nothing to do
+ if (bufferCount == mBufferCount)
+ return OK;
+
+ if (!mClientBufferCount &&
+ bufferCount >= mBufferCount) {
+ // easy, we just have more buffers
+ mBufferCount = bufferCount;
+ mServerBufferCount = bufferCount;
+ mDequeueCondition.signal();
+ } else {
+ // we're here because we're either
+ // - reducing the number of available buffers
+ // - or there is a client-buffer-count in effect
+
+ // less than 2 buffers is never allowed
+ if (bufferCount < 2)
+ return BAD_VALUE;
+
+ // when there is non client-buffer-count in effect, the client is not
+ // allowed to dequeue more than one buffer at a time,
+ // so the next time they dequeue a buffer, we know that they don't
+ // own one. the actual resizing will happen during the next
+ // dequeueBuffer.
+
+ mServerBufferCount = bufferCount;
+ }
+ return OK;
+}
+
+status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
+ Mutex::Autolock lock(mMutex);
+ return setBufferCountServerLocked(bufferCount);
+}
+
status_t SurfaceTexture::setBufferCount(int bufferCount) {
LOGV("SurfaceTexture::setBufferCount");
+ Mutex::Autolock lock(mMutex);
- if (bufferCount < MIN_BUFFER_SLOTS) {
+ // Error out if the user has dequeued buffers
+ for (int i=0 ; i<mBufferCount ; i++) {
+ if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
+ LOGE("setBufferCount: client owns some buffers");
+ return -EINVAL;
+ }
+ }
+
+ if (bufferCount == 0) {
+ const int minBufferSlots = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+ mClientBufferCount = 0;
+ bufferCount = (mServerBufferCount >= minBufferSlots) ?
+ mServerBufferCount : minBufferSlots;
+ return setBufferCountServerLocked(bufferCount);
+ }
+
+ // We don't allow the client to set a buffer-count less than
+ // MIN_ASYNC_BUFFER_SLOTS (3), there is no reason for it.
+ if (bufferCount < MIN_ASYNC_BUFFER_SLOTS) {
return BAD_VALUE;
}
- Mutex::Autolock lock(mMutex);
+ // here we're guaranteed that the client doesn't have dequeued buffers
+ // and will release all of its buffer references.
freeAllBuffers();
mBufferCount = bufferCount;
+ mClientBufferCount = bufferCount;
mCurrentTexture = INVALID_BUFFER_SLOT;
- mLastQueued = INVALID_BUFFER_SLOT;
+ mQueue.clear();
+ mDequeueCondition.signal();
return OK;
}
@@ -133,8 +190,7 @@
return OK;
}
-sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
- uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
+sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf) {
LOGV("SurfaceTexture::requestBuffer");
Mutex::Autolock lock(mMutex);
if (buf < 0 || mBufferCount <= buf) {
@@ -142,11 +198,123 @@
mBufferCount, buf);
return 0;
}
+ mSlots[buf].mRequestBufferCalled = true;
+ return mSlots[buf].mGraphicBuffer;
+}
+
+status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
+ uint32_t format, uint32_t usage) {
+ LOGV("SurfaceTexture::dequeueBuffer");
+
if ((w && !h) || (!w & h)) {
- LOGE("requestBuffer: invalid size: w=%u, h=%u: %d", w, h, buf);
- return 0;
+ LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
+ return BAD_VALUE;
}
+ Mutex::Autolock lock(mMutex);
+
+ status_t returnFlags(OK);
+
+ int found, foundSync;
+ int dequeuedCount = 0;
+ bool tryAgain = true;
+ while (tryAgain) {
+ // We need to wait for the FIFO to drain if the number of buffer
+ // needs to change.
+ //
+ // The condition "number of buffer needs to change" is true if
+ // - the client doesn't care about how many buffers there are
+ // - AND the actual number of buffer is different from what was
+ // set in the last setBufferCountServer()
+ // - OR -
+ // setBufferCountServer() was set to a value incompatible with
+ // the synchronization mode (for instance because the sync mode
+ // changed since)
+ //
+ // As long as this condition is true AND the FIFO is not empty, we
+ // wait on mDequeueCondition.
+
+ int minBufferCountNeeded = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+
+ if (!mClientBufferCount &&
+ ((mServerBufferCount != mBufferCount) ||
+ (mServerBufferCount < minBufferCountNeeded))) {
+ // wait for the FIFO to drain
+ while (!mQueue.isEmpty()) {
+ mDequeueCondition.wait(mMutex);
+ }
+ minBufferCountNeeded = mSynchronousMode ?
+ MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
+ }
+
+
+ if (!mClientBufferCount &&
+ ((mServerBufferCount != mBufferCount) ||
+ (mServerBufferCount < minBufferCountNeeded))) {
+ // here we're guaranteed that mQueue is empty
+ freeAllBuffers();
+ mBufferCount = mServerBufferCount;
+ if (mBufferCount < minBufferCountNeeded)
+ mBufferCount = minBufferCountNeeded;
+ mCurrentTexture = INVALID_BUFFER_SLOT;
+ returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
+ }
+
+ // look for a free buffer to give to the client
+ found = INVALID_BUFFER_SLOT;
+ foundSync = INVALID_BUFFER_SLOT;
+ dequeuedCount = 0;
+ for (int i = 0; i < mBufferCount; i++) {
+ const int state = mSlots[i].mBufferState;
+ if (state == BufferSlot::DEQUEUED) {
+ dequeuedCount++;
+ }
+ if (state == BufferSlot::FREE || i == mCurrentTexture) {
+ foundSync = i;
+ if (i != mCurrentTexture) {
+ found = i;
+ break;
+ }
+ }
+ }
+
+ // clients are not allowed to dequeue more than one buffer
+ // if they didn't set a buffer count.
+ if (!mClientBufferCount && dequeuedCount) {
+ return -EINVAL;
+ }
+
+ // make sure the client is not trying to dequeue more buffers
+ // than allowed.
+ const int avail = mBufferCount - (dequeuedCount+1);
+ if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
+ LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded (dequeued=%d)",
+ MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
+ dequeuedCount);
+ return -EBUSY;
+ }
+
+ // we're in synchronous mode and didn't find a buffer, we need to wait
+ // for for some buffers to be consumed
+ tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
+ if (tryAgain) {
+ mDequeueCondition.wait(mMutex);
+ }
+ }
+
+ if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
+ // foundSync guaranteed to be != INVALID_BUFFER_SLOT
+ found = foundSync;
+ }
+
+ if (found == INVALID_BUFFER_SLOT) {
+ return -EBUSY;
+ }
+
+ const int buf = found;
+ *outBuf = found;
+
const bool useDefaultSize = !w && !h;
if (useDefaultSize) {
// use the default size
@@ -160,78 +328,115 @@
format = mPixelFormat;
}
- usage |= GraphicBuffer::USAGE_HW_TEXTURE;
- sp<GraphicBuffer> graphicBuffer(
- mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
- if (graphicBuffer == 0) {
- LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed");
- } else {
- mUseDefaultSize = useDefaultSize;
+ // buffer is now in DEQUEUED (but can also be current at the same time,
+ // if we're in synchronous mode)
+ mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
+
+ const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
+ if ((buffer == NULL) ||
+ (uint32_t(buffer->width) != w) ||
+ (uint32_t(buffer->height) != h) ||
+ (uint32_t(buffer->format) != format) ||
+ ((uint32_t(buffer->usage) & usage) != usage))
+ {
+ usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+ sp<GraphicBuffer> graphicBuffer(
+ mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
+ if (graphicBuffer == 0) {
+ LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer failed");
+ return NO_MEMORY;
+ }
if (updateFormat) {
mPixelFormat = format;
}
mSlots[buf].mGraphicBuffer = graphicBuffer;
+ mSlots[buf].mRequestBufferCalled = false;
if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage);
mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
}
+ returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
}
- return graphicBuffer;
+ return returnFlags;
}
-status_t SurfaceTexture::dequeueBuffer(int *buf) {
- LOGV("SurfaceTexture::dequeueBuffer");
+status_t SurfaceTexture::setSynchronousMode(bool enabled) {
Mutex::Autolock lock(mMutex);
- int found = INVALID_BUFFER_SLOT;
- for (int i = 0; i < mBufferCount; i++) {
- if (!mSlots[i].mOwnedByClient && i != mCurrentTexture && i != mLastQueued) {
- mSlots[i].mOwnedByClient = true;
- found = i;
- break;
+
+ status_t err = OK;
+ if (!enabled) {
+ // going to asynchronous mode, drain the queue
+ while (mSynchronousMode != enabled && !mQueue.isEmpty()) {
+ mDequeueCondition.wait(mMutex);
}
}
- if (found == INVALID_BUFFER_SLOT) {
- return -EBUSY;
- }
- *buf = found;
-
- const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
- if (buffer == NULL) {
- return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+ if (mSynchronousMode != enabled) {
+ // - if we're going to asynchronous mode, the queue is guaranteed to be
+ // empty here
+ // - if the client set the number of buffers, we're guaranteed that
+ // we have at least 3 (because we don't allow less)
+ mSynchronousMode = enabled;
+ mDequeueCondition.signal();
}
-
- if ((mUseDefaultSize) &&
- ((uint32_t(buffer->width) != mDefaultWidth) ||
- (uint32_t(buffer->height) != mDefaultHeight))) {
- return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
- }
- return OK;
+ return err;
}
status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) {
LOGV("SurfaceTexture::queueBuffer");
+
+ sp<FrameAvailableListener> listener;
+
+ { // scope for the lock
Mutex::Autolock lock(mMutex);
- if (buf < 0 || mBufferCount <= buf) {
+ if (buf < 0 || buf >= mBufferCount) {
LOGE("queueBuffer: slot index out of range [0, %d]: %d",
mBufferCount, buf);
return -EINVAL;
- } else if (!mSlots[buf].mOwnedByClient) {
- LOGE("queueBuffer: slot %d is not owned by the client", buf);
+ } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+ LOGE("queueBuffer: slot %d is not owned by the client (state=%d)",
+ buf, mSlots[buf].mBufferState);
return -EINVAL;
- } else if (mSlots[buf].mGraphicBuffer == 0) {
+ } else if (buf == mCurrentTexture) {
+ LOGE("queueBuffer: slot %d is current!", buf);
+ return -EINVAL;
+ } else if (!mSlots[buf].mRequestBufferCalled) {
LOGE("queueBuffer: slot %d was enqueued without requesting a buffer",
buf);
return -EINVAL;
}
- mSlots[buf].mOwnedByClient = false;
- mLastQueued = buf;
- mLastQueuedCrop = mNextCrop;
- mLastQueuedTransform = mNextTransform;
- mLastQueuedTimestamp = timestamp;
- if (mFrameAvailableListener != 0) {
- mFrameAvailableListener->onFrameAvailable();
+
+ if (mQueue.empty()) {
+ listener = mFrameAvailableListener;
+ }
+
+ if (mSynchronousMode) {
+ // in synchronous mode we queue all buffers in a FIFO
+ mQueue.push_back(buf);
+ } else {
+ // in asynchronous mode we only keep the most recent buffer
+ if (mQueue.empty()) {
+ mQueue.push_back(buf);
+ } else {
+ Fifo::iterator front(mQueue.begin());
+ // buffer currently queued is freed
+ mSlots[*front].mBufferState = BufferSlot::FREE;
+ // and we record the new buffer index in the queued list
+ *front = buf;
+ }
+ }
+
+ mSlots[buf].mBufferState = BufferSlot::QUEUED;
+ mSlots[buf].mLastQueuedCrop = mNextCrop;
+ mSlots[buf].mLastQueuedTransform = mNextTransform;
+ mSlots[buf].mLastQueuedTimestamp = timestamp;
+ mDequeueCondition.signal();
+ } // scope for the lock
+
+ // call back without lock held
+ if (listener != 0) {
+ listener->onFrameAvailable();
}
return OK;
}
@@ -239,15 +444,17 @@
void SurfaceTexture::cancelBuffer(int buf) {
LOGV("SurfaceTexture::cancelBuffer");
Mutex::Autolock lock(mMutex);
- if (buf < 0 || mBufferCount <= buf) {
- LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount,
- buf);
+ if (buf < 0 || buf >= mBufferCount) {
+ LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
+ mBufferCount, buf);
return;
- } else if (!mSlots[buf].mOwnedByClient) {
- LOGE("cancelBuffer: slot %d is not owned by the client", buf);
+ } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+ LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
+ buf, mSlots[buf].mBufferState);
return;
}
- mSlots[buf].mOwnedByClient = false;
+ mSlots[buf].mBufferState = BufferSlot::FREE;
+ mDequeueCondition.signal();
}
status_t SurfaceTexture::setCrop(const Rect& crop) {
@@ -266,18 +473,31 @@
status_t SurfaceTexture::updateTexImage() {
LOGV("SurfaceTexture::updateTexImage");
+
Mutex::Autolock lock(mMutex);
- // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT,
+ int buf = mCurrentTexture;
+ if (!mQueue.empty()) {
+ // in asynchronous mode the list is guaranteed to be one buffer deep,
+ // while in synchronous mode we use the oldest buffer
+ Fifo::iterator front(mQueue.begin());
+ buf = *front;
+ mQueue.erase(front);
+ if (mQueue.isEmpty()) {
+ mDequeueCondition.signal();
+ }
+ }
+
+ // Initially both mCurrentTexture and buf are INVALID_BUFFER_SLOT,
// so this check will fail until a buffer gets queued.
- if (mCurrentTexture != mLastQueued) {
+ if (mCurrentTexture != buf) {
// Update the GL texture object.
- EGLImageKHR image = mSlots[mLastQueued].mEglImage;
+ EGLImageKHR image = mSlots[buf].mEglImage;
if (image == EGL_NO_IMAGE_KHR) {
EGLDisplay dpy = eglGetCurrentDisplay();
- image = createImage(dpy, mSlots[mLastQueued].mGraphicBuffer);
- mSlots[mLastQueued].mEglImage = image;
- mSlots[mLastQueued].mEglDisplay = dpy;
+ image = createImage(dpy, mSlots[buf].mGraphicBuffer);
+ mSlots[buf].mEglImage = image;
+ mSlots[buf].mEglDisplay = dpy;
if (image == EGL_NO_IMAGE_KHR) {
// NOTE: if dpy was invalid, createImage() is guaranteed to
// fail. so we'd end up here.
@@ -287,11 +507,10 @@
GLint error;
while ((error = glGetError()) != GL_NO_ERROR) {
- LOGE("GL error cleared before updating SurfaceTexture: %#04x", error);
+ LOGW("updateTexImage: clearing GL error: %#04x", error);
}
- GLenum target = getTextureTarget(
- mSlots[mLastQueued].mGraphicBuffer->format);
+ GLenum target = getTextureTarget(mSlots[buf].mGraphicBuffer->format);
if (target != mCurrentTextureTarget) {
glDeleteTextures(1, &mTexName);
}
@@ -301,20 +520,29 @@
bool failed = false;
while ((error = glGetError()) != GL_NO_ERROR) {
LOGE("error binding external texture image %p (slot %d): %#04x",
- image, mLastQueued, error);
+ image, buf, error);
failed = true;
}
if (failed) {
return -EINVAL;
}
+ if (mCurrentTexture != INVALID_BUFFER_SLOT) {
+ // the current buffer becomes FREE if it was still in the queued
+ // state. If it has already been given to the client
+ // (synchronous mode), then it stays in DEQUEUED state.
+ if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED)
+ mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE;
+ }
+
// Update the SurfaceTexture state.
- mCurrentTexture = mLastQueued;
+ mCurrentTexture = buf;
mCurrentTextureTarget = target;
- mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
- mCurrentCrop = mLastQueuedCrop;
- mCurrentTransform = mLastQueuedTransform;
- mCurrentTimestamp = mLastQueuedTimestamp;
+ mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
+ mCurrentCrop = mSlots[buf].mLastQueuedCrop;
+ mCurrentTransform = mSlots[buf].mLastQueuedTransform;
+ mCurrentTimestamp = mSlots[buf].mLastQueuedTimestamp;
+ mDequeueCondition.signal();
} else {
// We always bind the texture even if we don't update its contents.
glBindTexture(mCurrentTextureTarget, mTexName);
@@ -322,6 +550,11 @@
return OK;
}
+size_t SurfaceTexture::getQueuedCount() const {
+ Mutex::Autolock lock(mMutex);
+ return mQueue.size();
+}
+
bool SurfaceTexture::isExternalFormat(uint32_t format)
{
switch (format) {
@@ -470,7 +703,7 @@
void SurfaceTexture::freeAllBuffers() {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].mGraphicBuffer = 0;
- mSlots[i].mOwnedByClient = false;
+ mSlots[i].mBufferState = BufferSlot::FREE;
if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
@@ -510,6 +743,98 @@
return mCurrentTransform;
}
+int SurfaceTexture::query(int what, int* outValue)
+{
+ Mutex::Autolock lock(mMutex);
+ int value;
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ value = mDefaultWidth;
+ if (!mDefaultWidth && !mDefaultHeight && mCurrentTextureBuf!=0)
+ value = mCurrentTextureBuf->width;
+ break;
+ case NATIVE_WINDOW_HEIGHT:
+ value = mDefaultHeight;
+ if (!mDefaultWidth && !mDefaultHeight && mCurrentTextureBuf!=0)
+ value = mCurrentTextureBuf->height;
+ break;
+ case NATIVE_WINDOW_FORMAT:
+ value = mPixelFormat;
+ break;
+ case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+ value = mSynchronousMode ?
+ (MIN_UNDEQUEUED_BUFFERS-1) : MIN_UNDEQUEUED_BUFFERS;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+ outValue[0] = value;
+ return NO_ERROR;
+}
+
+void SurfaceTexture::dump(String8& result) const
+{
+ char buffer[1024];
+ dump(result, "", buffer, 1024);
+}
+
+void SurfaceTexture::dump(String8& result, const char* prefix,
+ char* buffer, size_t SIZE) const
+{
+ Mutex::Autolock _l(mMutex);
+ snprintf(buffer, SIZE,
+ "%smBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
+ "mPixelFormat=%d, mTexName=%d\n",
+ prefix, mBufferCount, mSynchronousMode, mDefaultWidth, mDefaultHeight,
+ mPixelFormat, mTexName);
+ result.append(buffer);
+
+ String8 fifo;
+ int fifoSize = 0;
+ Fifo::const_iterator i(mQueue.begin());
+ while (i != mQueue.end()) {
+ snprintf(buffer, SIZE, "%02d ", *i++);
+ fifoSize++;
+ fifo.append(buffer);
+ }
+
+ snprintf(buffer, SIZE,
+ "%scurrent: {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d, target=0x%04x}\n"
+ "%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, FIFO(%d)={%s}}\n"
+ ,
+ prefix, mCurrentCrop.left,
+ mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
+ mCurrentTransform, mCurrentTexture, mCurrentTextureTarget,
+ prefix, mNextCrop.left, mNextCrop.top, mNextCrop.right, mNextCrop.bottom,
+ mCurrentTransform, fifoSize, fifo.string()
+ );
+ result.append(buffer);
+
+ struct {
+ const char * operator()(int state) const {
+ switch (state) {
+ case BufferSlot::DEQUEUED: return "DEQUEUED";
+ case BufferSlot::QUEUED: return "QUEUED";
+ case BufferSlot::FREE: return "FREE";
+ default: return "Unknown";
+ }
+ }
+ } stateName;
+
+ for (int i=0 ; i<mBufferCount ; i++) {
+ const BufferSlot& slot(mSlots[i]);
+ snprintf(buffer, SIZE,
+ "%s%s[%02d] state=%-8s, crop=[%d,%d,%d,%d], transform=0x%02x, "
+ "timestamp=%lld\n"
+ ,
+ prefix, (i==mCurrentTexture)?">":" ", i, stateName(slot.mBufferState),
+ slot.mLastQueuedCrop.left, slot.mLastQueuedCrop.top,
+ slot.mLastQueuedCrop.right, slot.mLastQueuedCrop.bottom,
+ slot.mLastQueuedTransform, slot.mLastQueuedTimestamp
+ );
+ result.append(buffer);
+ }
+}
static void mtxMul(float out[16], const float a[16], const float b[16]) {
out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index ec6da43..6f10320 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -39,6 +39,9 @@
ANativeWindow::query = query;
ANativeWindow::perform = perform;
+ const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
+ const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
+
// Get a reference to the allocator.
mAllocator = mSurfaceTexture->getAllocator();
}
@@ -90,27 +93,40 @@
}
int SurfaceTextureClient::setSwapInterval(int interval) {
- return INVALID_OPERATION;
+ // EGL specification states:
+ // interval is silently clamped to minimum and maximum implementation
+ // dependent values before being stored.
+ // Although we don't have to, we apply the same logic here.
+
+ if (interval < minSwapInterval)
+ interval = minSwapInterval;
+
+ if (interval > maxSwapInterval)
+ interval = maxSwapInterval;
+
+ status_t res = mSurfaceTexture->setSynchronousMode(interval ? true : false);
+
+ return res;
}
int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
LOGV("SurfaceTextureClient::dequeueBuffer");
Mutex::Autolock lock(mMutex);
int buf = -1;
- status_t err = mSurfaceTexture->dequeueBuffer(&buf);
- if (err < 0) {
- LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer failed: %d", err);
- return err;
+ status_t result = mSurfaceTexture->dequeueBuffer(&buf, mReqWidth, mReqHeight,
+ mReqFormat, mReqUsage);
+ if (result < 0) {
+ LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
+ "failed: %d", result, mReqWidth, mReqHeight, mReqFormat, mReqUsage);
+ return result;
}
sp<GraphicBuffer>& gbuf(mSlots[buf]);
- if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION ||
- gbuf == 0 ||
- (mReqWidth && gbuf->getWidth() != mReqWidth) ||
- (mReqHeight && gbuf->getHeight() != mReqHeight) ||
- (mReqFormat && uint32_t(gbuf->getPixelFormat()) != mReqFormat) ||
- (gbuf->getUsage() & mReqUsage) != mReqUsage) {
- gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight,
- mReqFormat, mReqUsage);
+ if (result & ISurfaceTexture::RELEASE_ALL_BUFFERS) {
+ freeAllBuffers();
+ }
+
+ if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
+ gbuf = mSurfaceTexture->requestBuffer(buf);
if (gbuf == 0) {
LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed");
return NO_MEMORY;
@@ -163,29 +179,17 @@
int SurfaceTextureClient::query(int what, int* value) const {
LOGV("SurfaceTextureClient::query");
- Mutex::Autolock lock(mMutex);
switch (what) {
- case NATIVE_WINDOW_WIDTH:
- *value = mQueryWidth ? mQueryWidth : mReqWidth;
- return NO_ERROR;
- case NATIVE_WINDOW_HEIGHT:
- *value = mQueryHeight ? mQueryHeight : mReqHeight;
- return NO_ERROR;
- case NATIVE_WINDOW_FORMAT:
- *value = mQueryFormat ? mQueryFormat : mReqFormat;
- return NO_ERROR;
- case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
- *value = MIN_UNDEQUEUED_BUFFERS;
- return NO_ERROR;
case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
- // SurfaceTextureClient currently never queues frames to SurfaceFlinger.
+ // TODO: this is not needed anymore
*value = 0;
return NO_ERROR;
case NATIVE_WINDOW_CONCRETE_TYPE:
+ // TODO: this is not needed anymore
*value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT;
return NO_ERROR;
}
- return BAD_VALUE;
+ return mSurfaceTexture->query(what, value);
}
int SurfaceTextureClient::perform(int operation, va_list args)
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 753e933..59a4cc5 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -17,23 +17,78 @@
#include <EGL/egl.h>
#include <gtest/gtest.h>
#include <gui/SurfaceTextureClient.h>
+#include <utils/threads.h>
namespace android {
class SurfaceTextureClientTest : public ::testing::Test {
protected:
+ SurfaceTextureClientTest():
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglSurface(EGL_NO_SURFACE),
+ mEglContext(EGL_NO_CONTEXT) {
+ }
+
virtual void SetUp() {
mST = new SurfaceTexture(123);
mSTC = new SurfaceTextureClient(mST);
+
+ // We need a valid GL context so we can test updateTexImage()
+ // This initializes EGL and create a dummy GL context with a
+ // pbuffer render target.
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+ EGLint majorVersion, minorVersion;
+ EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLConfig myConfig;
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(),
+ &myConfig, 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLint pbufferAttribs[] = {
+ EGL_WIDTH, 16,
+ EGL_HEIGHT, 16,
+ EGL_NONE };
+ mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig, pbufferAttribs);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+ mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
}
virtual void TearDown() {
mST.clear();
mSTC.clear();
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(mEglDisplay, mEglContext);
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ eglTerminate(mEglDisplay);
+ }
+
+ virtual EGLint const* getConfigAttribs() {
+ static EGLint sDefaultConfigAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_NONE
+ };
+
+ return sDefaultConfigAttribs;
}
sp<SurfaceTexture> mST;
sp<SurfaceTextureClient> mSTC;
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLContext mEglContext;
};
TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
@@ -94,8 +149,8 @@
EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, anw.get(),
NULL);
- ASSERT_NE(EGL_NO_SURFACE, eglSurface);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ EXPECT_NE(EGL_NO_SURFACE, eglSurface);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
eglTerminate(dpy);
}
@@ -204,6 +259,7 @@
sp<ANativeWindow> anw(mSTC);
sp<SurfaceTexture> st(mST);
ANativeWindowBuffer* buf[2];
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
EXPECT_NE(buf[0], buf[1]);
@@ -225,6 +281,7 @@
sp<ANativeWindow> anw(mSTC);
sp<SurfaceTexture> st(mST);
ANativeWindowBuffer* buf[2];
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
@@ -247,4 +304,203 @@
ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
}
+TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(false));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(OK, st->updateTexImage());
+
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(OK, st->updateTexImage());
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ EXPECT_NE(buf[0], buf[1]);
+ EXPECT_NE(buf[1], buf[2]);
+ EXPECT_NE(buf[2], buf[0]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ EXPECT_NE(buf[0], buf[1]);
+ EXPECT_NE(buf[1], buf[2]);
+ EXPECT_NE(buf[2], buf[0]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]);
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ EXPECT_NE(buf[0], buf[1]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ EXPECT_NE(buf[1], buf[2]);
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDequeueCurrent) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ android_native_buffer_t* firstBuf;
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &firstBuf));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), firstBuf));
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), firstBuf);
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ EXPECT_NE(buf[0], buf[1]);
+ EXPECT_NE(buf[1], buf[2]);
+ EXPECT_NE(buf[2], buf[0]);
+ EXPECT_EQ(firstBuf, buf[2]);
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+ EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ EXPECT_EQ(-EBUSY, anw->dequeueBuffer(anw.get(), &buf[2]));
+
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+
+ EXPECT_EQ(OK, st->updateTexImage());
+ EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]);
+
+ EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[2]));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeWaitRetire) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+
+ class MyThread : public Thread {
+ sp<SurfaceTexture> st;
+ EGLContext ctx;
+ EGLSurface sur;
+ EGLDisplay dpy;
+ bool mBufferRetired;
+ Mutex mLock;
+ virtual bool threadLoop() {
+ eglMakeCurrent(dpy, sur, sur, ctx);
+ usleep(20000);
+ Mutex::Autolock _l(mLock);
+ st->updateTexImage();
+ mBufferRetired = true;
+ eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ return false;
+ }
+ public:
+ MyThread(const sp<SurfaceTexture>& st)
+ : st(st), mBufferRetired(false) {
+ ctx = eglGetCurrentContext();
+ sur = eglGetCurrentSurface(EGL_DRAW);
+ dpy = eglGetCurrentDisplay();
+ eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ }
+ ~MyThread() {
+ eglMakeCurrent(dpy, sur, sur, ctx);
+ }
+ void bufferDequeued() {
+ Mutex::Autolock _l(mLock);
+ EXPECT_EQ(true, mBufferRetired);
+ }
+ };
+
+ android_native_buffer_t* buf[3];
+ ASSERT_EQ(OK, st->setSynchronousMode(true));
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3));
+ // dequeue/queue/update so we have a current buffer
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ st->updateTexImage();
+
+ MyThread* thread = new MyThread(st);
+ sp<Thread> threadBase(thread);
+
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1]));
+ thread->run();
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2]));
+ thread->bufferDequeued();
+ thread->requestExitAndWait();
+}
+
}
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index e41dd39..22034c5 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -681,6 +681,9 @@
delete ass;
}
}
+ if (idmap != NULL) {
+ delete idmap;
+ }
}
if (required && !rt) LOGW("Unable to find resources file resources.arsc");
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index adb3174..f963058 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -20,12 +20,15 @@
#include <utils/ByteOrder.h>
#include <stdio.h>
+#include <string.h>
#include <unistd.h>
#include <cutils/log.h>
namespace android {
+static const bool DEBUG = false;
+
/*
* File Format (v1):
*
@@ -75,6 +78,7 @@
paddingSize = padding_extra(n);
if (paddingSize > 0) {
uint32_t padding = 0xbcbcbcbc;
+ if (DEBUG) LOGI("writing %d padding bytes for %d", paddingSize, n);
amt = write(m_fd, &padding, paddingSize);
if (amt != paddingSize) {
m_status = errno;
@@ -107,8 +111,8 @@
} else {
k = key;
}
- if (false) {
- LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(),
+ if (DEBUG) {
+ LOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(),
dataSize);
}
@@ -121,6 +125,7 @@
header.keyLen = tolel(keyLen);
header.dataSize = tolel(dataSize);
+ if (DEBUG) LOGI("writing entity header, %d bytes", sizeof(entity_header_v1));
amt = write(m_fd, &header, sizeof(entity_header_v1));
if (amt != sizeof(entity_header_v1)) {
m_status = errno;
@@ -128,6 +133,7 @@
}
m_pos += amt;
+ if (DEBUG) LOGI("writing entity header key, %d bytes", keyLen+1);
amt = write(m_fd, k.string(), keyLen+1);
if (amt != keyLen+1) {
m_status = errno;
@@ -145,7 +151,12 @@
status_t
BackupDataWriter::WriteEntityData(const void* data, size_t size)
{
+ if (DEBUG) LOGD("Writing data: size=%lu", (unsigned long) size);
+
if (m_status != NO_ERROR) {
+ if (DEBUG) {
+ LOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status));
+ }
return m_status;
}
@@ -155,6 +166,7 @@
ssize_t amt = write(m_fd, data, size);
if (amt != (ssize_t)size) {
m_status = errno;
+ if (DEBUG) LOGD("write returned error %d (%s)", m_status, strerror(m_status));
return m_status;
}
m_pos += amt;
diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp
index 4ad9b51..ad4a308 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -442,6 +442,184 @@
return 0;
}
+// Utility function, equivalent to stpcpy(): perform a strcpy, but instead of
+// returning the initial dest, return a pointer to the trailing NUL.
+static char* strcpy_ptr(char* dest, const char* str) {
+ if (dest && str) {
+ while ((*dest = *str) != 0) {
+ dest++;
+ str++;
+ }
+ }
+ return dest;
+}
+
+int write_tarfile(const String8& packageName, const String8& domain,
+ const String8& rootpath, const String8& filepath, BackupDataWriter* writer)
+{
+ // In the output stream everything is stored relative to the root
+ const char* relstart = filepath.string() + rootpath.length();
+ if (*relstart == '/') relstart++; // won't be true when path == rootpath
+ String8 relpath(relstart);
+
+ // Too long a name for the ustar format?
+ // "apps/" + packagename + '/' + domainpath < 155 chars
+ // relpath < 100 chars
+ if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) {
+ LOGE("Filename [%s] too long, skipping", relpath.string());
+ return -1;
+ }
+
+ int err = 0;
+ struct stat64 s;
+ if (lstat64(filepath.string(), &s) != 0) {
+ err = errno;
+ LOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string());
+ return err;
+ }
+
+ const int isdir = S_ISDIR(s.st_mode);
+
+ // !!! TODO: use mmap when possible to avoid churning the buffer cache
+ // !!! TODO: this will break with symlinks; need to use readlink(2)
+ int fd = open(filepath.string(), O_RDONLY);
+ if (fd < 0) {
+ err = errno;
+ LOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string());
+ return err;
+ }
+
+ // read/write up to this much at a time.
+ const size_t BUFSIZE = 32 * 1024;
+
+ char* buf = new char[BUFSIZE];
+ if (buf == NULL) {
+ LOGE("Out of mem allocating transfer buffer");
+ err = ENOMEM;
+ goto done;
+ }
+
+ // Good to go -- first construct the standard tar header at the start of the buffer
+ memset(buf, 0, 512); // tar header is 512 bytes
+
+ // Magic fields for the ustar file format
+ strcat(buf + 257, "ustar");
+ strcat(buf + 263, "00");
+
+ {
+ // Prefix and main relative path. Path lengths have been preflighted.
+
+ // [ 345 : 155 ] filename path prefix [ustar]
+ //
+ // packagename and domain can each be empty.
+ char* cp = buf + 345;
+ if (packageName.length() > 0) {
+ // it's an app; so prefix with "apps/packagename/"
+ cp = strcpy_ptr(cp, "apps/");
+ cp = strcpy_ptr(cp, packageName.string());
+ }
+
+ if (domain.length() > 0) {
+ // only need a / if there was a package name
+ if (packageName.length() > 0) *cp++ = '/';
+ cp = strcpy_ptr(cp, domain.string());
+ }
+
+ // [ 0 : 100 ]; file name/path
+ strncpy(buf, relpath.string(), 100);
+
+ LOGI(" Name: %s/%s", buf + 345, buf);
+ }
+
+ // [ 100 : 8 ] file mode
+ snprintf(buf + 100, 8, "0%o", s.st_mode);
+
+ // [ 108 : 8 ] uid -- ignored in Android format; uids are remapped at restore time
+ // [ 116 : 8 ] gid -- ignored in Android format
+ snprintf(buf + 108, 8, "0%lo", s.st_uid);
+ snprintf(buf + 116, 8, "0%lo", s.st_gid);
+
+ // [ 124 : 12 ] file size in bytes
+ snprintf(buf + 124, 12, "0%llo", s.st_size);
+
+ // [ 136 : 12 ] last mod time as a UTC time_t
+ snprintf(buf + 136, 12, "%0lo", s.st_mtime);
+
+ // [ 148 : 8 ] checksum -- to be calculated with this field as space chars
+ memset(buf + 148, ' ', 8);
+
+ // [ 156 : 1 ] link/file type
+ uint8_t type;
+ if (isdir) {
+ type = '5'; // tar magic: '5' == directory
+ } else if (S_ISREG(s.st_mode)) {
+ type = '0'; // tar magic: '0' == normal file
+ } else {
+ LOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string());
+ goto cleanup;
+ }
+ buf[156] = type;
+
+ // [ 157 : 100 ] name of linked file [not implemented]
+
+ // Now go back and calculate the header checksum
+ {
+ uint16_t sum = 0;
+ for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) {
+ sum += *p;
+ }
+
+ // Now write the real checksum value:
+ // [ 148 : 8 ] checksum: 6 octal digits [leading zeroes], NUL, SPC
+ sprintf(buf + 148, "%06o", sum); // the trailing space is already in place
+ }
+
+ // Write the 512-byte tar file header block to the output
+ writer->WriteEntityData(buf, 512);
+
+ // Now write the file data itself, for real files. We honor tar's convention that
+ // only full 512-byte blocks are sent to write().
+ if (!isdir) {
+ off64_t toWrite = s.st_size;
+ while (toWrite > 0) {
+ size_t toRead = (toWrite < BUFSIZE) ? toWrite : BUFSIZE;
+ ssize_t nRead = read(fd, buf, toRead);
+ if (nRead < 0) {
+ err = errno;
+ LOGE("Unable to read file [%s], err=%d (%s)", filepath.string(),
+ err, strerror(err));
+ break;
+ } else if (nRead == 0) {
+ LOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite,
+ filepath.string());
+ err = EIO;
+ break;
+ }
+
+ // At EOF we might have a short block; NUL-pad that to a 512-byte multiple. This
+ // depends on the OS guarantee that for ordinary files, read() will never return
+ // less than the number of bytes requested.
+ ssize_t partial = (nRead+512) % 512;
+ if (partial > 0) {
+ ssize_t remainder = 512 - partial;
+ memset(buf + nRead, 0, remainder);
+ nRead += remainder;
+ }
+ writer->WriteEntityData(buf, nRead);
+ toWrite -= nRead;
+ }
+ }
+
+cleanup:
+ delete [] buf;
+done:
+ close(fd);
+ return err;
+}
+// end tarfile
+
+
+
#define RESTORE_BUF_SIZE (8*1024)
RestoreHelperBase::RestoreHelperBase()
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 0d03361..03db8d7 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -338,6 +338,10 @@
nativeWindow(window), buffer(0), previousBuffer(0), module(0),
bits(NULL)
{
+ hw_module_t const* pModule;
+ hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule);
+ module = reinterpret_cast<gralloc_module_t const*>(pModule);
+
pixelFormatTable = gglGetPixelFormatTable();
// keep a reference on the window
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 2502f15..da26229 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -179,7 +179,8 @@
__eglMustCastToProperFunctionPointerType* curr,
getProcAddressType getProcAddress)
{
- char scrap[256];
+ const size_t SIZE = 256;
+ char scrap[SIZE];
while (*api) {
char const * name = *api;
__eglMustCastToProperFunctionPointerType f =
@@ -191,7 +192,7 @@
if (f == NULL) {
// Try without the OES postfix
ssize_t index = ssize_t(strlen(name)) - 3;
- if ((index>0 && (index<255)) && (!strcmp(name+index, "OES"))) {
+ if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) {
strncpy(scrap, name, index);
scrap[index] = 0;
f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);
@@ -201,10 +202,8 @@
if (f == NULL) {
// Try with the OES postfix
ssize_t index = ssize_t(strlen(name)) - 3;
- if ((index>0 && (index<252)) && (strcmp(name+index, "OES"))) {
- strncpy(scrap, name, index);
- scrap[index] = 0;
- strcat(scrap, "OES");
+ if (index>0 && strcmp(name+index, "OES")) {
+ snprintf(scrap, SIZE, "%sOES", name);
f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);
//LOGD_IF(f, "found <%s> instead", scrap);
}