move lock/unlock implementaion outside of Surface into SurfaceTextureClient

This makes ANativeWindow_lock/ANativeWindow_unlockAndPost work
with ANativeWindows implemented by Surface and SurfaceTextureClient.

Also, Surface now inherits directly from SurfaceTextureClient.

Bug: 5003724
Change-Id: I9f285877c7bae9a262e9a7af91c2bae78804b2ef
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index 5ec469e..cfe2aa1 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -21,6 +21,7 @@
 #include <gui/SurfaceTexture.h>
 
 #include <ui/egl/android_natives.h>
+#include <ui/Region.h>
 
 #include <utils/RefBase.h>
 #include <utils/threads.h>
@@ -37,29 +38,24 @@
 
     sp<ISurfaceTexture> getISurfaceTexture() const;
 
-private:
-    friend class Surface;
+protected:
+    SurfaceTextureClient();
+    void setISurfaceTexture(const sp<ISurfaceTexture>& surfaceTexture);
 
+private:
     // can't be copied
     SurfaceTextureClient& operator = (const SurfaceTextureClient& rhs);
     SurfaceTextureClient(const SurfaceTextureClient& rhs);
+    void init();
 
     // ANativeWindow hooks
-    static int cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
-    static int dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer);
-    static int lockBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
-    static int perform(ANativeWindow* window, int operation, ...);
-    static int query(const ANativeWindow* window, int what, int* value);
-    static int queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
-    static int setSwapInterval(ANativeWindow* window, int interval);
-
-    int cancelBuffer(ANativeWindowBuffer* buffer);
-    int dequeueBuffer(ANativeWindowBuffer** buffer);
-    int lockBuffer(ANativeWindowBuffer* buffer);
-    int perform(int operation, va_list args);
-    int query(int what, int* value) const;
-    int queueBuffer(ANativeWindowBuffer* buffer);
-    int setSwapInterval(int interval);
+    static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
+    static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer);
+    static int hook_lockBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
+    static int hook_perform(ANativeWindow* window, int operation, ...);
+    static int hook_query(const ANativeWindow* window, int what, int* value);
+    static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
+    static int hook_setSwapInterval(ANativeWindow* window, int interval);
 
     int dispatchConnect(va_list args);
     int dispatchDisconnect(va_list args);
@@ -71,26 +67,38 @@
     int dispatchSetBuffersTimestamp(va_list args);
     int dispatchSetCrop(va_list args);
     int dispatchSetUsage(va_list args);
+    int dispatchLock(va_list args);
+    int dispatchUnlockAndPost(va_list args);
 
-    int connect(int api);
-    int disconnect(int api);
-    int setBufferCount(int bufferCount);
-    int setBuffersDimensions(int w, int h);
-    int setBuffersFormat(int format);
-    int setBuffersTransform(int transform);
-    int setBuffersTimestamp(int64_t timestamp);
-    int setCrop(Rect const* rect);
-    int setUsage(uint32_t reqUsage);
+protected:
+    virtual int cancelBuffer(ANativeWindowBuffer* buffer);
+    virtual int dequeueBuffer(ANativeWindowBuffer** buffer);
+    virtual int lockBuffer(ANativeWindowBuffer* buffer);
+    virtual int perform(int operation, va_list args);
+    virtual int query(int what, int* value) const;
+    virtual int queueBuffer(ANativeWindowBuffer* buffer);
+    virtual int setSwapInterval(int interval);
 
-    void freeAllBuffers();
-    int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
-
-    int getConnectedApi() const;
+    virtual int connect(int api);
+    virtual int disconnect(int api);
+    virtual int setBufferCount(int bufferCount);
+    virtual int setBuffersDimensions(int w, int h);
+    virtual int setBuffersFormat(int format);
+    virtual int setBuffersTransform(int transform);
+    virtual int setBuffersTimestamp(int64_t timestamp);
+    virtual int setCrop(Rect const* rect);
+    virtual int setUsage(uint32_t reqUsage);
+    virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
+    virtual int unlockAndPost();
 
     enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS };
     enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS };
     enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
 
+private:
+    void freeAllBuffers();
+    int getSlotFromBufferLocked(android_native_buffer_t* buffer) const;
+
     // mSurfaceTexture is the interface to the surface texture server. All
     // operations on the surface texture client ultimately translate into
     // interactions with the server using this interface.
@@ -145,6 +153,12 @@
     // variables of SurfaceTexture objects. It must be locked whenever the
     // member variables are accessed.
     mutable Mutex mMutex;
+
+    // must be used from the lock/unlock thread
+    sp<GraphicBuffer>           mLockedBuffer;
+    sp<GraphicBuffer>           mPostedBuffer;
+    mutable Region              mOldDirtyRegion;
+    bool                        mConnectedToCpu;
 };
 
 }; // namespace android
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index dc2a845..c2a494d 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -28,6 +28,8 @@
 #include <ui/Region.h>
 #include <ui/egl/android_natives.h>
 
+#include <gui/SurfaceTextureClient.h>
+
 #include <surfaceflinger/ISurface.h>
 #include <surfaceflinger/ISurfaceComposerClient.h>
 
@@ -37,14 +39,9 @@
 
 // ---------------------------------------------------------------------------
 
-class GraphicBuffer;
-class GraphicBufferMapper;
-class IOMX;
 class ISurfaceTexture;
-class Rect;
 class Surface;
 class SurfaceComposerClient;
-class SurfaceTextureClient;
 
 // ---------------------------------------------------------------------------
 
@@ -129,8 +126,7 @@
     
 // ---------------------------------------------------------------------------
 
-class Surface 
-    : public EGLNativeBase<ANativeWindow, Surface, RefBase>
+class Surface : public SurfaceTextureClient
 {
 public:
     struct SurfaceInfo {
@@ -158,32 +154,14 @@
     sp<ISurfaceTexture> getSurfaceTexture();
 
     // the lock/unlock APIs must be used from the same thread
-    status_t    lock(SurfaceInfo* info, bool blocking = true);
-    status_t    lock(SurfaceInfo* info, Region* dirty, bool blocking = true);
+    status_t    lock(SurfaceInfo* info, Region* dirty = NULL);
     status_t    unlockAndPost();
 
     sp<IBinder> asBinder() const;
 
 private:
-    /*
-     * Android frameworks friends
-     * (eventually this should go away and be replaced by proper APIs)
-     */
-    // camera and camcorder need access to the ISurface binder interface for preview
-    friend class CameraService;
-    friend class MediaRecorder;
-    // MediaPlayer needs access to ISurface for display
-    friend class MediaPlayer;
-    friend class IOMX;
-    friend class SoftwareRenderer;
     // this is just to be able to write some unit tests
     friend class Test;
-    // videoEditor preview classes
-    friend class VideoEditorPreviewController;
-    friend class PreviewRenderer;
-
-private:
-    friend class SurfaceComposerClient;
     friend class SurfaceControl;
 
     // can't be copied
@@ -194,62 +172,27 @@
     Surface(const Parcel& data, const sp<IBinder>& ref);
     ~Surface();
 
-
-    /*
-     *  ANativeWindow hooks
-     */
-    static int setSwapInterval(ANativeWindow* window, int interval);
-    static int dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer);
-    static int cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
-    static int lockBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
-    static int queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
-    static int query(const ANativeWindow* window, int what, int* value);
-    static int perform(ANativeWindow* window, int operation, ...);
-
-    int setSwapInterval(int interval);
-    int dequeueBuffer(ANativeWindowBuffer** buffer);
-    int lockBuffer(ANativeWindowBuffer* buffer);
-    int queueBuffer(ANativeWindowBuffer* buffer);
-    int cancelBuffer(ANativeWindowBuffer* buffer);
-    int query(int what, int* value) const;
-    int perform(int operation, va_list args);
-
     /*
      *  private stuff...
      */
     void init();
     status_t validate(bool inCancelBuffer = false) const;
 
-    int getConnectedApi() const;
-    
     static void cleanCachedSurfacesLocked();
 
+    virtual int query(int what, int* value) const;
+
     // constants
     status_t                    mInitCheck;
     sp<ISurface>                mSurface;
-    sp<SurfaceTextureClient>    mSurfaceTextureClient;
     uint32_t                    mIdentity;
     PixelFormat                 mFormat;
     uint32_t                    mFlags;
-    
-    // protected by mSurfaceLock. These are also used from lock/unlock
-    // but in that case, they must be called form the same thread.
-    mutable Region              mDirtyRegion;
-
-    // must be used from the lock/unlock thread
-    sp<GraphicBuffer>           mLockedBuffer;
-    sp<GraphicBuffer>           mPostedBuffer;
-    mutable Region              mOldDirtyRegion;
-    bool                        mReserved;
 
     // query() must be called from dequeueBuffer() thread
     uint32_t                    mWidth;
     uint32_t                    mHeight;
 
-    // Inherently thread-safe
-    mutable Mutex               mSurfaceLock;
-    mutable Mutex               mApiLock;
-
     // A cache of Surface objects that have been deserialized into this process.
     static Mutex sCachedSurfacesLock;
     static DefaultKeyedVector<wp<IBinder>, wp<Surface> > sCachedSurfaces;
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 9185e1e..dabe643 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -46,59 +46,6 @@
 
 namespace android {
 
-// ----------------------------------------------------------------------
-
-static status_t copyBlt(
-        const sp<GraphicBuffer>& dst, 
-        const sp<GraphicBuffer>& src, 
-        const Region& reg)
-{
-    // src and dst with, height and format must be identical. no verification
-    // is done here.
-    status_t err;
-    uint8_t const * src_bits = NULL;
-    err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits);
-    LOGE_IF(err, "error locking src buffer %s", strerror(-err));
-
-    uint8_t* dst_bits = NULL;
-    err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits);
-    LOGE_IF(err, "error locking dst buffer %s", strerror(-err));
-
-    Region::const_iterator head(reg.begin());
-    Region::const_iterator tail(reg.end());
-    if (head != tail && src_bits && dst_bits) {
-        const size_t bpp = bytesPerPixel(src->format);
-        const size_t dbpr = dst->stride * bpp;
-        const size_t sbpr = src->stride * bpp;
-
-        while (head != tail) {
-            const Rect& r(*head++);
-            ssize_t h = r.height();
-            if (h <= 0) continue;
-            size_t size = r.width() * bpp;
-            uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp;
-            uint8_t       * d = dst_bits + (r.left + dst->stride * r.top) * bpp;
-            if (dbpr==sbpr && size==sbpr) {
-                size *= h;
-                h = 1;
-            }
-            do {
-                memcpy(d, s, size);
-                d += dbpr;
-                s += sbpr;
-            } while (--h > 0);
-        }
-    }
-    
-    if (src_bits)
-        src->unlock();
-    
-    if (dst_bits)
-        dst->unlock();
-    
-    return err;
-}
-
 // ============================================================================
 //  SurfaceControl
 // ============================================================================
@@ -277,7 +224,8 @@
 // ---------------------------------------------------------------------------
 
 Surface::Surface(const sp<SurfaceControl>& surface)
-    : mInitCheck(NO_INIT),
+    : SurfaceTextureClient(),
+      mInitCheck(NO_INIT),
       mSurface(surface->mSurface),
       mIdentity(surface->mIdentity),
       mFormat(surface->mFormat), mFlags(surface->mFlags),
@@ -287,7 +235,8 @@
 }
 
 Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
-    : mInitCheck(NO_INIT)
+    : SurfaceTextureClient(),
+      mInitCheck(NO_INIT)
 {
     mSurface    = interface_cast<ISurface>(ref);
     mIdentity   = parcel.readInt32();
@@ -363,36 +312,21 @@
 
 void Surface::init()
 {
-    ANativeWindow::setSwapInterval  = setSwapInterval;
-    ANativeWindow::dequeueBuffer    = dequeueBuffer;
-    ANativeWindow::cancelBuffer     = cancelBuffer;
-    ANativeWindow::lockBuffer       = lockBuffer;
-    ANativeWindow::queueBuffer      = queueBuffer;
-    ANativeWindow::query            = query;
-    ANativeWindow::perform          = perform;
-
     if (mSurface != NULL) {
         sp<ISurfaceTexture> surfaceTexture(mSurface->getSurfaceTexture());
         LOGE_IF(surfaceTexture==0, "got a NULL ISurfaceTexture from ISurface");
         if (surfaceTexture != NULL) {
-            mSurfaceTextureClient = new SurfaceTextureClient(surfaceTexture);
-            mSurfaceTextureClient->setUsage(GraphicBuffer::USAGE_HW_RENDER);
+            setISurfaceTexture(surfaceTexture);
+            setUsage(GraphicBuffer::USAGE_HW_RENDER);
         }
 
         DisplayInfo dinfo;
         SurfaceComposerClient::getDisplayInfo(0, &dinfo);
         const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi;
         const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi;
-
-        const_cast<int&>(ANativeWindow::minSwapInterval) =
-                mSurfaceTextureClient->minSwapInterval;
-
-        const_cast<int&>(ANativeWindow::maxSwapInterval) =
-                mSurfaceTextureClient->maxSwapInterval;
-
         const_cast<uint32_t&>(ANativeWindow::flags) = 0;
 
-        if (mSurfaceTextureClient != 0) {
+        if (surfaceTexture != NULL) {
             mInitCheck = NO_ERROR;
         }
     }
@@ -402,7 +336,6 @@
 {
     // clear all references and trigger an IPC now, to make sure things
     // happen without delay, since these resources are quite heavy.
-    mSurfaceTextureClient.clear();
     mSurface.clear();
     IPCThreadState::self()->flushCommands();
 }
@@ -431,77 +364,6 @@
 
 // ----------------------------------------------------------------------------
 
-int Surface::setSwapInterval(ANativeWindow* window, int interval) {
-    Surface* self = getSelf(window);
-    return self->setSwapInterval(interval);
-}
-
-int Surface::dequeueBuffer(ANativeWindow* window, 
-        ANativeWindowBuffer** buffer) {
-    Surface* self = getSelf(window);
-    return self->dequeueBuffer(buffer);
-}
-
-int Surface::cancelBuffer(ANativeWindow* window,
-        ANativeWindowBuffer* buffer) {
-    Surface* self = getSelf(window);
-    return self->cancelBuffer(buffer);
-}
-
-int Surface::lockBuffer(ANativeWindow* window, 
-        ANativeWindowBuffer* buffer) {
-    Surface* self = getSelf(window);
-    return self->lockBuffer(buffer);
-}
-
-int Surface::queueBuffer(ANativeWindow* window, 
-        ANativeWindowBuffer* buffer) {
-    Surface* self = getSelf(window);
-    return self->queueBuffer(buffer);
-}
-
-int Surface::query(const ANativeWindow* window,
-        int what, int* value) {
-    const Surface* self = getSelf(window);
-    return self->query(what, value);
-}
-
-int Surface::perform(ANativeWindow* window, 
-        int operation, ...) {
-    va_list args;
-    va_start(args, operation);
-    Surface* self = getSelf(window);
-    int res = self->perform(operation, args);
-    va_end(args);
-    return res;
-}
-
-// ----------------------------------------------------------------------------
-
-int Surface::setSwapInterval(int interval) {
-    return mSurfaceTextureClient->setSwapInterval(interval);
-}
-
-int Surface::dequeueBuffer(ANativeWindowBuffer** buffer) {
-    status_t err = mSurfaceTextureClient->dequeueBuffer(buffer);
-    if (err == NO_ERROR) {
-        mDirtyRegion.set(buffer[0]->width, buffer[0]->height);
-    }
-    return err;
-}
-
-int Surface::cancelBuffer(ANativeWindowBuffer* buffer) {
-    return mSurfaceTextureClient->cancelBuffer(buffer);
-}
-
-int Surface::lockBuffer(ANativeWindowBuffer* buffer) {
-    return mSurfaceTextureClient->lockBuffer(buffer);
-}
-
-int Surface::queueBuffer(ANativeWindowBuffer* buffer) {
-    return mSurfaceTextureClient->queueBuffer(buffer);
-}
-
 int Surface::query(int what, int* value) const {
     switch (what) {
     case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
@@ -509,141 +371,39 @@
         *value = 1;
         return NO_ERROR;
     case NATIVE_WINDOW_CONCRETE_TYPE:
-        // TODO: this is not needed anymore
         *value = NATIVE_WINDOW_SURFACE;
         return NO_ERROR;
     }
-    return mSurfaceTextureClient->query(what, value);
-}
-
-int Surface::perform(int operation, va_list args) {
-    return mSurfaceTextureClient->perform(operation, args);
+    return SurfaceTextureClient::query(what, value);
 }
 
 // ----------------------------------------------------------------------------
 
-int Surface::getConnectedApi() const {
-    return mSurfaceTextureClient->getConnectedApi();
-}
+status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn) {
+    ANativeWindow_Buffer outBuffer;
 
-// ----------------------------------------------------------------------------
-
-status_t Surface::lock(SurfaceInfo* info, bool blocking) {
-    return Surface::lock(info, NULL, blocking);
-}
-
-status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) 
-{
-    if (getConnectedApi()) {
-        LOGE("Surface::lock(%p) failed. Already connected to another API",
-                (ANativeWindow*)this);
-        CallStack stack;
-        stack.update();
-        stack.dump("");
-        return INVALID_OPERATION;
+    ARect temp;
+    ARect* inOutDirtyBounds = NULL;
+    if (dirtyIn) {
+        temp = dirtyIn->getBounds();
+        inOutDirtyBounds = &temp;
     }
 
-    if (mApiLock.tryLock() != NO_ERROR) {
-        LOGE("calling Surface::lock from different threads!");
-        CallStack stack;
-        stack.update();
-        stack.dump("");
-        return WOULD_BLOCK;
-    }
+    status_t err = SurfaceTextureClient::lock(&outBuffer, inOutDirtyBounds);
 
-    /* Here we're holding mApiLock */
-    
-    if (mLockedBuffer != 0) {
-        LOGE("Surface::lock failed, already locked");
-        mApiLock.unlock();
-        return INVALID_OPERATION;
-    }
-
-    // we're intending to do software rendering from this point
-    mSurfaceTextureClient->setUsage(
-            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
-
-    ANativeWindowBuffer* out;
-    status_t err = mSurfaceTextureClient->dequeueBuffer(&out);
-    LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
     if (err == NO_ERROR) {
-        sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
-        err = mSurfaceTextureClient->lockBuffer(backBuffer.get());
-        LOGE_IF(err, "lockBuffer (handle=%p) failed (%s)",
-                backBuffer->handle, strerror(-err));
-        if (err == NO_ERROR) {
-            const Rect bounds(backBuffer->width, backBuffer->height);
-            const Region boundsRegion(bounds);
-            Region scratch(boundsRegion);
-            Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch);
-            newDirtyRegion &= boundsRegion;
-
-            // figure out if we can copy the frontbuffer back
-            const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
-            const bool canCopyBack = (frontBuffer != 0 &&
-                    backBuffer->width  == frontBuffer->width &&
-                    backBuffer->height == frontBuffer->height &&
-                    backBuffer->format == frontBuffer->format &&
-                    !(mFlags & ISurfaceComposer::eDestroyBackbuffer));
-
-            // the dirty region we report to surfaceflinger is the one
-            // given by the user (as opposed to the one *we* return to the
-            // user).
-            mDirtyRegion = newDirtyRegion;
-
-            if (canCopyBack) {
-                // copy the area that is invalid and not repainted this round
-                const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion));
-                if (!copyback.isEmpty())
-                    copyBlt(backBuffer, frontBuffer, copyback);
-            } else {
-                // if we can't copy-back anything, modify the user's dirty
-                // region to make sure they redraw the whole buffer
-                newDirtyRegion = boundsRegion;
-            }
-
-            // keep track of the are of the buffer that is "clean"
-            // (ie: that will be redrawn)
-            mOldDirtyRegion = newDirtyRegion;
-
-            void* vaddr;
-            status_t res = backBuffer->lock(
-                    GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                    newDirtyRegion.bounds(), &vaddr);
-            
-            LOGW_IF(res, "failed locking buffer (handle = %p)", 
-                    backBuffer->handle);
-
-            mLockedBuffer = backBuffer;
-            other->w      = backBuffer->width;
-            other->h      = backBuffer->height;
-            other->s      = backBuffer->stride;
-            other->usage  = backBuffer->usage;
-            other->format = backBuffer->format;
-            other->bits   = vaddr;
-        }
+        other->w = uint32_t(outBuffer.width);
+        other->h = uint32_t(outBuffer.height);
+        other->s = uint32_t(outBuffer.stride);
+        other->usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
+        other->format = uint32_t(outBuffer.format);
+        other->bits = outBuffer.bits;
     }
-    mApiLock.unlock();
     return err;
 }
-    
-status_t Surface::unlockAndPost() 
-{
-    if (mLockedBuffer == 0) {
-        LOGE("Surface::unlockAndPost failed, no locked buffer");
-        return INVALID_OPERATION;
-    }
 
-    status_t err = mLockedBuffer->unlock();
-    LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
-    
-    err = mSurfaceTextureClient->queueBuffer(mLockedBuffer.get());
-    LOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
-            mLockedBuffer->handle, strerror(-err));
-
-    mPostedBuffer = mLockedBuffer;
-    mLockedBuffer = 0;
-    return err;
+status_t Surface::unlockAndPost() {
+    return SurfaceTextureClient::unlockAndPost();
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 1410481..a12d40a 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -495,7 +495,7 @@
 }
 
 status_t SurfaceTexture::connect(int api) {
-    LOGV("SurfaceTexture::connect");
+    LOGV("SurfaceTexture::connect(this=%p, %d)", this, api);
     Mutex::Autolock lock(mMutex);
     int err = NO_ERROR;
     switch (api) {
@@ -504,6 +504,8 @@
         case NATIVE_WINDOW_API_MEDIA:
         case NATIVE_WINDOW_API_CAMERA:
             if (mConnectedApi != NO_CONNECTED_API) {
+                LOGE("connect: already connected (cur=%d, req=%d)",
+                        mConnectedApi, api);
                 err = -EINVAL;
             } else {
                 mConnectedApi = api;
@@ -517,7 +519,7 @@
 }
 
 status_t SurfaceTexture::disconnect(int api) {
-    LOGV("SurfaceTexture::disconnect");
+    LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api);
     Mutex::Autolock lock(mMutex);
     int err = NO_ERROR;
     switch (api) {
@@ -528,6 +530,8 @@
             if (mConnectedApi == api) {
                 mConnectedApi = NO_CONNECTED_API;
             } else {
+                LOGE("disconnect: connected to another api (cur=%d, req=%d)",
+                        mConnectedApi, api);
                 err = -EINVAL;
             }
             break;
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index f39cabf..d5b7c89 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -24,24 +24,45 @@
 namespace android {
 
 SurfaceTextureClient::SurfaceTextureClient(
-        const sp<ISurfaceTexture>& surfaceTexture):
-        mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0),
-        mReqHeight(0), mReqFormat(0), mReqUsage(0),
-        mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO),
-        mQueryWidth(0), mQueryHeight(0), mQueryFormat(0),
-        mMutex() {
+        const sp<ISurfaceTexture>& surfaceTexture)
+{
+    SurfaceTextureClient::init();
+    SurfaceTextureClient::setISurfaceTexture(surfaceTexture);
+}
+
+SurfaceTextureClient::SurfaceTextureClient() {
+    SurfaceTextureClient::init();
+}
+
+void SurfaceTextureClient::init() {
     // Initialize the ANativeWindow function pointers.
-    ANativeWindow::setSwapInterval  = setSwapInterval;
-    ANativeWindow::dequeueBuffer    = dequeueBuffer;
-    ANativeWindow::cancelBuffer     = cancelBuffer;
-    ANativeWindow::lockBuffer       = lockBuffer;
-    ANativeWindow::queueBuffer      = queueBuffer;
-    ANativeWindow::query            = query;
-    ANativeWindow::perform          = perform;
+    ANativeWindow::setSwapInterval  = hook_setSwapInterval;
+    ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;
+    ANativeWindow::cancelBuffer     = hook_cancelBuffer;
+    ANativeWindow::lockBuffer       = hook_lockBuffer;
+    ANativeWindow::queueBuffer      = hook_queueBuffer;
+    ANativeWindow::query            = hook_query;
+    ANativeWindow::perform          = hook_perform;
 
     const_cast<int&>(ANativeWindow::minSwapInterval) = 0;
     const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
 
+    mReqWidth = 0;
+    mReqHeight = 0;
+    mReqFormat = 0;
+    mReqUsage = 0;
+    mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
+    mQueryWidth = 0;
+    mQueryHeight = 0;
+    mQueryFormat = 0;
+    mConnectedToCpu = false;
+}
+
+void SurfaceTextureClient::setISurfaceTexture(
+        const sp<ISurfaceTexture>& surfaceTexture)
+{
+    mSurfaceTexture = surfaceTexture;
+
     // Get a reference to the allocator.
     mAllocator = mSurfaceTexture->getAllocator();
 }
@@ -50,42 +71,42 @@
     return mSurfaceTexture;
 }
 
-int SurfaceTextureClient::setSwapInterval(ANativeWindow* window, int interval) {
+int SurfaceTextureClient::hook_setSwapInterval(ANativeWindow* window, int interval) {
     SurfaceTextureClient* c = getSelf(window);
     return c->setSwapInterval(interval);
 }
 
-int SurfaceTextureClient::dequeueBuffer(ANativeWindow* window,
+int SurfaceTextureClient::hook_dequeueBuffer(ANativeWindow* window,
         ANativeWindowBuffer** buffer) {
     SurfaceTextureClient* c = getSelf(window);
     return c->dequeueBuffer(buffer);
 }
 
-int SurfaceTextureClient::cancelBuffer(ANativeWindow* window,
+int SurfaceTextureClient::hook_cancelBuffer(ANativeWindow* window,
         ANativeWindowBuffer* buffer) {
     SurfaceTextureClient* c = getSelf(window);
     return c->cancelBuffer(buffer);
 }
 
-int SurfaceTextureClient::lockBuffer(ANativeWindow* window,
+int SurfaceTextureClient::hook_lockBuffer(ANativeWindow* window,
         ANativeWindowBuffer* buffer) {
     SurfaceTextureClient* c = getSelf(window);
     return c->lockBuffer(buffer);
 }
 
-int SurfaceTextureClient::queueBuffer(ANativeWindow* window,
+int SurfaceTextureClient::hook_queueBuffer(ANativeWindow* window,
         ANativeWindowBuffer* buffer) {
     SurfaceTextureClient* c = getSelf(window);
     return c->queueBuffer(buffer);
 }
 
-int SurfaceTextureClient::query(const ANativeWindow* window,
+int SurfaceTextureClient::hook_query(const ANativeWindow* window,
                                 int what, int* value) {
     const SurfaceTextureClient* c = getSelf(window);
     return c->query(what, value);
 }
 
-int SurfaceTextureClient::perform(ANativeWindow* window, int operation, ...) {
+int SurfaceTextureClient::hook_perform(ANativeWindow* window, int operation, ...) {
     va_list args;
     va_start(args, operation);
     SurfaceTextureClient* c = getSelf(window);
@@ -219,7 +240,6 @@
         *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;
     }
@@ -260,6 +280,12 @@
     case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
         res = dispatchSetBuffersFormat(args);
         break;
+    case NATIVE_WINDOW_LOCK:
+        res = dispatchLock(args);
+        break;
+    case NATIVE_WINDOW_UNLOCK_AND_POST:
+        res = dispatchUnlockAndPost(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -324,28 +350,37 @@
     return setBuffersTimestamp(timestamp);
 }
 
+int SurfaceTextureClient::dispatchLock(va_list args) {
+    ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*);
+    ARect* inOutDirtyBounds = va_arg(args, ARect*);
+    return lock(outBuffer, inOutDirtyBounds);
+}
+
+int SurfaceTextureClient::dispatchUnlockAndPost(va_list args) {
+    return unlockAndPost();
+}
+
+
 int SurfaceTextureClient::connect(int api) {
     LOGV("SurfaceTextureClient::connect");
     Mutex::Autolock lock(mMutex);
-    return mSurfaceTexture->connect(api);
+    int err = mSurfaceTexture->connect(api);
+    if (!err && api == NATIVE_WINDOW_API_CPU) {
+        mConnectedToCpu = true;
+    }
+    return err;
 }
 
 int SurfaceTextureClient::disconnect(int api) {
     LOGV("SurfaceTextureClient::disconnect");
     Mutex::Autolock lock(mMutex);
-    return mSurfaceTexture->disconnect(api);
+    int err = mSurfaceTexture->disconnect(api);
+    if (!err && api == NATIVE_WINDOW_API_CPU) {
+        mConnectedToCpu = false;
+    }
+    return err;
 }
 
-int SurfaceTextureClient::getConnectedApi() const
-{
-    // XXX: This method will be going away shortly, and is currently bogus.  It
-    // always returns "nothing is connected".  It will go away once Surface gets
-    // updated to actually connect as the 'CPU' API when locking a buffer.
-    Mutex::Autolock lock(mMutex);
-    return 0;
-}
-
-
 int SurfaceTextureClient::setUsage(uint32_t reqUsage)
 {
     LOGV("SurfaceTextureClient::setUsage");
@@ -443,4 +478,160 @@
     }
 }
 
+// ----------------------------------------------------------------------
+// the lock/unlock APIs must be used from the same thread
+
+static status_t copyBlt(
+        const sp<GraphicBuffer>& dst,
+        const sp<GraphicBuffer>& src,
+        const Region& reg)
+{
+    // src and dst with, height and format must be identical. no verification
+    // is done here.
+    status_t err;
+    uint8_t const * src_bits = NULL;
+    err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits);
+    LOGE_IF(err, "error locking src buffer %s", strerror(-err));
+
+    uint8_t* dst_bits = NULL;
+    err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits);
+    LOGE_IF(err, "error locking dst buffer %s", strerror(-err));
+
+    Region::const_iterator head(reg.begin());
+    Region::const_iterator tail(reg.end());
+    if (head != tail && src_bits && dst_bits) {
+        const size_t bpp = bytesPerPixel(src->format);
+        const size_t dbpr = dst->stride * bpp;
+        const size_t sbpr = src->stride * bpp;
+
+        while (head != tail) {
+            const Rect& r(*head++);
+            ssize_t h = r.height();
+            if (h <= 0) continue;
+            size_t size = r.width() * bpp;
+            uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp;
+            uint8_t       * d = dst_bits + (r.left + dst->stride * r.top) * bpp;
+            if (dbpr==sbpr && size==sbpr) {
+                size *= h;
+                h = 1;
+            }
+            do {
+                memcpy(d, s, size);
+                d += dbpr;
+                s += sbpr;
+            } while (--h > 0);
+        }
+    }
+
+    if (src_bits)
+        src->unlock();
+
+    if (dst_bits)
+        dst->unlock();
+
+    return err;
+}
+
+// ----------------------------------------------------------------------------
+
+status_t SurfaceTextureClient::lock(
+        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
+{
+    if (mLockedBuffer != 0) {
+        LOGE("Surface::lock failed, already locked");
+        return INVALID_OPERATION;
+    }
+
+    if (!mConnectedToCpu) {
+        int err = SurfaceTextureClient::connect(NATIVE_WINDOW_API_CPU);
+        if (err) {
+            return err;
+        }
+        // we're intending to do software rendering from this point
+        setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+    }
+
+    ANativeWindowBuffer* out;
+    status_t err = dequeueBuffer(&out);
+    LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
+    if (err == NO_ERROR) {
+        sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
+        err = lockBuffer(backBuffer.get());
+        LOGE_IF(err, "lockBuffer (handle=%p) failed (%s)",
+                backBuffer->handle, strerror(-err));
+        if (err == NO_ERROR) {
+            const Rect bounds(backBuffer->width, backBuffer->height);
+
+            Region newDirtyRegion;
+            if (inOutDirtyBounds) {
+                newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds));
+                newDirtyRegion.andSelf(bounds);
+            } else {
+                newDirtyRegion.set(bounds);
+            }
+
+            // figure out if we can copy the frontbuffer back
+            const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
+            const bool canCopyBack = (frontBuffer != 0 &&
+                    backBuffer->width  == frontBuffer->width &&
+                    backBuffer->height == frontBuffer->height &&
+                    backBuffer->format == frontBuffer->format);
+
+            if (canCopyBack) {
+                // copy the area that is invalid and not repainted this round
+                const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion));
+                if (!copyback.isEmpty())
+                    copyBlt(backBuffer, frontBuffer, copyback);
+            } else {
+                // if we can't copy-back anything, modify the user's dirty
+                // region to make sure they redraw the whole buffer
+                newDirtyRegion.set(bounds);
+            }
+
+            // keep track of the are of the buffer that is "clean"
+            // (ie: that will be redrawn)
+            mOldDirtyRegion = newDirtyRegion;
+
+            if (inOutDirtyBounds) {
+                *inOutDirtyBounds = newDirtyRegion.getBounds();
+            }
+
+            void* vaddr;
+            status_t res = backBuffer->lock(
+                    GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                    newDirtyRegion.bounds(), &vaddr);
+
+            LOGW_IF(res, "failed locking buffer (handle = %p)",
+                    backBuffer->handle);
+
+            mLockedBuffer = backBuffer;
+            outBuffer->width  = backBuffer->width;
+            outBuffer->height = backBuffer->height;
+            outBuffer->stride = backBuffer->stride;
+            outBuffer->format = backBuffer->format;
+            outBuffer->bits   = vaddr;
+        }
+    }
+    return err;
+}
+
+status_t SurfaceTextureClient::unlockAndPost()
+{
+    if (mLockedBuffer == 0) {
+        LOGE("Surface::unlockAndPost failed, no locked buffer");
+        return INVALID_OPERATION;
+    }
+
+    status_t err = mLockedBuffer->unlock();
+    LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
+
+    err = queueBuffer(mLockedBuffer.get());
+    LOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
+            mLockedBuffer->handle, strerror(-err));
+
+    mPostedBuffer = mLockedBuffer;
+    mLockedBuffer = 0;
+    return err;
+}
+
 }; // namespace android
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 9c10c75..794747d 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -303,6 +303,10 @@
         case NATIVE_WINDOW_CONNECT:
         case NATIVE_WINDOW_DISCONNECT:
             break;
+        case NATIVE_WINDOW_LOCK:
+            return INVALID_OPERATION;
+        case NATIVE_WINDOW_UNLOCK_AND_POST:
+            return INVALID_OPERATION;
         default:
             return NAME_NOT_FOUND;
     }