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);
             }