Merge "Add timeout when waiting for HW vsync" into jb-mr1-dev
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
index 691ba2f..3378d97 100644
--- a/include/binder/IPCThreadState.h
+++ b/include/binder/IPCThreadState.h
@@ -41,7 +41,6 @@
 
             int                 getCallingPid();
             int                 getCallingUid();
-            int                 getOrigCallingUid();
 
             void                setStrictModePolicy(int32_t policy);
             int32_t             getStrictModePolicy() const;
@@ -117,7 +116,6 @@
             status_t            mLastError;
             pid_t               mCallingPid;
             uid_t               mCallingUid;
-            uid_t               mOrigCallingUid;
             int32_t             mStrictModePolicy;
             int32_t             mLastTransactionBinderFlags;
 };
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 877b17c..3ff95d2 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -285,9 +285,12 @@
             return err;
         }
     }
-    void* buffer = writeInplace(size);
-    return buffer == NULL ? NO_MEMORY :
-        val.flatten(buffer);
+    if (size) {
+        void* buffer = writeInplace(size);
+        return buffer == NULL ? NO_MEMORY :
+                val.flatten(buffer);
+    }
+    return NO_ERROR;
 }
 
 template<typename T>
@@ -303,9 +306,12 @@
         }
         size = s;
     }
-    void const* buffer = readInplace(size);
-    return buffer == NULL ? NO_MEMORY :
-        val.unflatten(buffer, size);
+    if (size) {
+        void const* buffer = readInplace(size);
+        return buffer == NULL ? NO_MEMORY :
+                val.unflatten(buffer, size);
+    }
+    return NO_ERROR;
 }
 
 // ---------------------------------------------------------------------------
diff --git a/include/ui/Region.h b/include/ui/Region.h
index f0819af..0049fde 100644
--- a/include/ui/Region.h
+++ b/include/ui/Region.h
@@ -28,6 +28,7 @@
 namespace android {
 // ---------------------------------------------------------------------------
 
+class SharedBuffer;
 class String8;
 
 // ---------------------------------------------------------------------------
@@ -41,10 +42,10 @@
                         
         Region& operator = (const Region& rhs);
 
-    inline  bool        isEmpty() const     { return mBounds.isEmpty();  }
-    inline  bool        isRect() const      { return mStorage.isEmpty(); }
+    inline  bool        isEmpty() const     { return getBounds().isEmpty(); }
+    inline  bool        isRect() const      { return mStorage.size() == 1; }
 
-    inline  Rect        getBounds() const   { return mBounds; }
+    inline  Rect        getBounds() const   { return mStorage[mStorage.size() - 1]; }
     inline  Rect        bounds() const      { return getBounds(); }
 
             // the region becomes its bounds
@@ -106,18 +107,24 @@
 
     
     /* various ways to access the rectangle list */
+
     
+    // STL-like iterators
     typedef Rect const* const_iterator;
-    
-            const_iterator begin() const;
-            const_iterator end() const;
+    const_iterator begin() const;
+    const_iterator end() const;
+
+    // returns an array of rect which has the same life-time has this
+    // Region object.
+    Rect const* getArray(size_t* count) const;
+
+    // returns a SharedBuffer as well as the number of rects.
+    // ownership is transfered to the caller.
+    // the caller must call SharedBuffer::release() to free the memory.
+    SharedBuffer const* getSharedBuffer(size_t* count) const;
 
     /* no user serviceable parts here... */
             
-            size_t      getRects(Vector<Rect>& rectList) const;
-            Rect const* getArray(size_t* count) const;
-
-            
             // add a rectangle to the internal list. This rectangle must
             // be sorted in Y and X and must not make the region invalid.
             void        addRectUnchecked(int l, int t, int r, int b);
@@ -156,8 +163,11 @@
 
     static bool validate(const Region& reg, const char* name);
     
-    Rect            mBounds;
-    Vector<Rect>    mStorage;
+    // mStorage is a (manually) sorted array of Rects describing the region
+    // with an extra Rect as the last element which is set to the
+    // bounds of the region. However, if the region is
+    // a simple Rect then mStorage contains only that rect.
+    Vector<Rect> mStorage;
 };
 
 
diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h
index 24508b0..b670953 100644
--- a/include/utils/SharedBuffer.h
+++ b/include/utils/SharedBuffer.h
@@ -44,9 +44,6 @@
      * users.
      */
     static          ssize_t                 dealloc(const SharedBuffer* released);
-    
-    //! get the SharedBuffer from the data pointer
-    static  inline  const SharedBuffer*     sharedBuffer(const void* data);
 
     //! access the data for read
     inline          const void*             data() const;
@@ -94,9 +91,10 @@
 private:
         inline SharedBuffer() { }
         inline ~SharedBuffer() { }
-        inline SharedBuffer(const SharedBuffer&);
+        SharedBuffer(const SharedBuffer&);
+        SharedBuffer& operator = (const SharedBuffer&);
  
-        // 16 bytes. must be sized to preserve correct alingment.
+        // 16 bytes. must be sized to preserve correct alignment.
         mutable int32_t        mRefs;
                 size_t         mSize;
                 uint32_t       mReserved[2];
@@ -104,10 +102,6 @@
 
 // ---------------------------------------------------------------------------
 
-const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) {
-    return data ? reinterpret_cast<const SharedBuffer *>(data)-1 : 0;
-}
-
 const void* SharedBuffer::data() const {
     return this + 1;
 }
@@ -120,19 +114,16 @@
     return mSize;
 }
 
-SharedBuffer* SharedBuffer::bufferFromData(void* data)
-{
-    return ((SharedBuffer*)data)-1;
+SharedBuffer* SharedBuffer::bufferFromData(void* data) {
+    return data ? static_cast<SharedBuffer *>(data)-1 : 0;
 }
     
-const SharedBuffer* SharedBuffer::bufferFromData(const void* data)
-{
-    return ((const SharedBuffer*)data)-1;
+const SharedBuffer* SharedBuffer::bufferFromData(const void* data) {
+    return data ? static_cast<const SharedBuffer *>(data)-1 : 0;
 }
 
-size_t SharedBuffer::sizeFromData(const void* data)
-{
-    return (((const SharedBuffer*)data)-1)->mSize;
+size_t SharedBuffer::sizeFromData(const void* data) {
+    return data ? bufferFromData(data)->mSize : 0;
 }
 
 bool SharedBuffer::onlyOwner() const {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 7e416b9..6e83faa 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -371,11 +371,6 @@
     return mCallingUid;
 }
 
-int IPCThreadState::getOrigCallingUid()
-{
-    return mOrigCallingUid;
-}
-
 int64_t IPCThreadState::clearCallingIdentity()
 {
     int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
@@ -646,7 +641,6 @@
 {
     pthread_setspecific(gTLS, this);
     clearCaller();
-    mOrigCallingUid = mCallingUid;
     mIn.setDataCapacity(256);
     mOut.setDataCapacity(256);
 }
@@ -998,7 +992,6 @@
             
             mCallingPid = tr.sender_pid;
             mCallingUid = tr.sender_euid;
-            mOrigCallingUid = tr.sender_euid;
             
             int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
             if (gDisableBackgroundScheduling) {
@@ -1056,7 +1049,6 @@
             
             mCallingPid = origPid;
             mCallingUid = origUid;
-            mOrigCallingUid = origUid;
 
             IF_LOG_TRANSACTIONS() {
                 TextOutput::Bundle _b(alog);
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index db021ff..4640149 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -812,6 +812,23 @@
 status_t BufferQueue::acquireBuffer(BufferItem *buffer) {
     ATRACE_CALL();
     Mutex::Autolock _l(mMutex);
+
+    // Check that the consumer doesn't currently have the maximum number of
+    // buffers acquired.  We allow the max buffer count to be exceeded by one
+    // buffer, so that the consumer can successfully set up the newly acquired
+    // buffer before releasing the old one.
+    int numAcquiredBuffers = 0;
+    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+        if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) {
+            numAcquiredBuffers++;
+        }
+    }
+    if (numAcquiredBuffers >= mMaxAcquiredBufferCount+1) {
+        ST_LOGE("acquireBuffer: max acquired buffer count reached: %d (max=%d)",
+                numAcquiredBuffers, mMaxAcquiredBufferCount);
+        return INVALID_OPERATION;
+    }
+
     // check if queue is empty
     // In asynchronous mode the list is guaranteed to be one buffer
     // deep, while in synchronous mode we use the oldest buffer.
diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp
index a70a5e8..139f219 100644
--- a/libs/gui/IGraphicBufferAlloc.cpp
+++ b/libs/gui/IGraphicBufferAlloc.cpp
@@ -55,7 +55,7 @@
         status_t result = reply.readInt32();
         if (result == NO_ERROR) {
             graphicBuffer = new GraphicBuffer();
-            reply.read(*graphicBuffer);
+            result = reply.read(*graphicBuffer);
             // reply.readStrongBinder();
             // here we don't even have to read the BufferReference from
             // the parcel, it'll die with the parcel.
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 9ed23be..975eb23 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -288,7 +288,9 @@
         computeCurrentTransformMatrix();
     } else  {
         if (err < 0) {
-            ST_LOGE("updateTexImage failed on acquire %d", err);
+            ST_LOGE("updateTexImage: acquire failed: %s (%d)",
+                strerror(-err), err);
+            return err;
         }
         // We always bind the texture even if we don't update its contents.
         glBindTexture(mTexTarget, mTexName);
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 59b9efd..ec14a0d 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -241,7 +241,7 @@
     EXPECT_EQ(1, buf->height);
     EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
     ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1));
-    EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 16, 8, 0));
+    EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 16, 8));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf));
     EXPECT_EQ(16, buf->width);
     EXPECT_EQ(8, buf->height);
@@ -597,15 +597,15 @@
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers
     mST->getTransformMatrix(mtx);
 
-    // This accounts for the 1 texel shrink for each edge that's included in the
+    // This accounts for the .5 texel shrink for each edge that's included in the
     // transform matrix to avoid texturing outside the crop region.
-    EXPECT_EQ(.375f, mtx[0]);
+    EXPECT_EQ(0.5, mtx[0]);
     EXPECT_EQ(0.f, mtx[1]);
     EXPECT_EQ(0.f, mtx[2]);
     EXPECT_EQ(0.f, mtx[3]);
 
     EXPECT_EQ(0.f, mtx[4]);
-    EXPECT_EQ(-.375f, mtx[5]);
+    EXPECT_EQ(-0.5, mtx[5]);
     EXPECT_EQ(0.f, mtx[6]);
     EXPECT_EQ(0.f, mtx[7]);
 
@@ -614,8 +614,8 @@
     EXPECT_EQ(1.f, mtx[10]);
     EXPECT_EQ(0.f, mtx[11]);
 
-    EXPECT_EQ(.125f, mtx[12]);
-    EXPECT_EQ(.5f, mtx[13]);
+    EXPECT_EQ(0.0625f, mtx[12]);
+    EXPECT_EQ(0.5625f, mtx[13]);
     EXPECT_EQ(0.f, mtx[14]);
     EXPECT_EQ(1.f, mtx[15]);
 }
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index 55b5968..212c6a7 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -711,7 +711,7 @@
     ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(),
             -1));
 
-    mST->updateTexImage();
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -757,7 +757,7 @@
     ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(),
             -1));
 
-    mST->updateTexImage();
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -817,7 +817,7 @@
         ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(),
                 buf->getNativeBuffer(), -1));
 
-        mST->updateTexImage();
+        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
         glClearColor(0.2, 0.2, 0.2, 0.2);
         glClear(GL_COLOR_BUFFER_BIT);
@@ -966,7 +966,7 @@
         if (i > 1) {
             mFW->waitForFrame();
         }
-        mST->updateTexImage();
+        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
         drawTexture();
 
         for (int j = 0; j < numTestPixels; j++) {
@@ -997,7 +997,7 @@
 
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
-    mST->updateTexImage();
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -1039,7 +1039,7 @@
 
     ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
 
-    mST->updateTexImage();
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -1196,7 +1196,7 @@
     EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
     EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1));
     mFW->waitForFrame();
-    EXPECT_EQ(OK,mST->updateTexImage());
+    EXPECT_EQ(OK, mST->updateTexImage());
     Rect r = mST->getCurrentCrop();
     assertRectEq(Rect(23, 78, 123, 477), r);
 
@@ -1230,7 +1230,7 @@
     EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
     EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1));
     mFW->waitForFrame();
-    EXPECT_EQ(OK,mST->updateTexImage());
+    EXPECT_EQ(OK, mST->updateTexImage());
     Rect r = mST->getCurrentCrop();
     // crop should be the same as crop (same aspect ratio)
     assertRectEq(Rect(10, 20, 330, 200), r);
@@ -1241,7 +1241,7 @@
     EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
     EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1));
     mFW->waitForFrame();
-    EXPECT_EQ(OK,mST->updateTexImage());
+    EXPECT_EQ(OK, mST->updateTexImage());
     r = mST->getCurrentCrop();
     // crop should be the same height, but have cropped left and right borders
     // offset is 30.6 px L+, R-
@@ -1253,7 +1253,7 @@
     EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb));
     EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1));
     mFW->waitForFrame();
-    EXPECT_EQ(OK,mST->updateTexImage());
+    EXPECT_EQ(OK, mST->updateTexImage());
     r = mST->getCurrentCrop();
     // crop should be the same width, but have cropped top and bottom borders
     // offset is 37.5 px
@@ -1420,6 +1420,9 @@
 
     mST->setDefaultBufferSize(texWidth, texHeight);
 
+    // This test requires 3 buffers to complete run on a single thread.
+    mST->setDefaultMaxBufferCount(3);
+
     // Do the producer side of things
     EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
             mProducerEglSurface, mProducerEglContext));
@@ -1453,8 +1456,9 @@
 
     glDisable(GL_SCISSOR_TEST);
 
-    mST->updateTexImage(); // Skip the first frame, which was empty
-    mST->updateTexImage();
+    // Skip the first frame, which was empty
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -1508,7 +1512,7 @@
                 mEglContext));
         ASSERT_EQ(EGL_SUCCESS, eglGetError());
         mFW->waitForFrame();
-        mST->updateTexImage();
+        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
         buffers[i] = mST->getCurrentBuffer();
     }
 
@@ -1611,6 +1615,9 @@
     enum { texWidth = 64 };
     enum { texHeight = 64 };
 
+    // This test requires 3 buffers to complete run on a single thread.
+    mST->setDefaultMaxBufferCount(3);
+
     // Set the user buffer size.
     native_window_set_buffers_user_dimensions(mANW.get(), texWidth, texHeight);
 
@@ -1639,8 +1646,9 @@
 
     glDisable(GL_SCISSOR_TEST);
 
-    mST->updateTexImage(); // Skip the first frame, which was empty
-    mST->updateTexImage();
+    // Skip the first frame, which was empty
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -1664,6 +1672,9 @@
     enum { texWidth = 64 };
     enum { texHeight = 16 };
 
+    // This test requires 3 buffers to complete run on a single thread.
+    mST->setDefaultMaxBufferCount(3);
+
     // Set the transform hint.
     mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90);
 
@@ -1696,8 +1707,9 @@
 
     glDisable(GL_SCISSOR_TEST);
 
-    mST->updateTexImage(); // Skip the first frame, which was empty
-    mST->updateTexImage();
+    // Skip the first frame, which was empty
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -1721,6 +1733,9 @@
     enum { texWidth = 64 };
     enum { texHeight = 16 };
 
+    // This test requires 3 buffers to complete run on a single thread.
+    mST->setDefaultMaxBufferCount(3);
+
     // Set the transform hint.
     mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90);
 
@@ -1753,8 +1768,9 @@
 
     glDisable(GL_SCISSOR_TEST);
 
-    mST->updateTexImage(); // Skip the first frame, which was empty
-    mST->updateTexImage();
+    // Skip the first frame, which was empty
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     glClearColor(0.2, 0.2, 0.2, 0.2);
     glClear(GL_COLOR_BUFFER_BIT);
@@ -1944,7 +1960,7 @@
     runProducerThread(new PT());
 
     mFC->waitForFrame();
-    mST->updateTexImage();
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
     mFC->finishFrame();
 
     // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
@@ -1964,7 +1980,7 @@
 
     mFC->waitForFrame();
     mFC->finishFrame();
-    mST->updateTexImage();
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
 }
@@ -1990,7 +2006,7 @@
     for (int i = 0; i < NUM_ITERATIONS; i++) {
         mFC->waitForFrame();
         ALOGV("+updateTexImage");
-        mST->updateTexImage();
+        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
         ALOGV("-updateTexImage");
         mFC->finishFrame();
 
@@ -2020,7 +2036,7 @@
         mFC->waitForFrame();
         mFC->finishFrame();
         ALOGV("+updateTexImage");
-        mST->updateTexImage();
+        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
         ALOGV("-updateTexImage");
 
         // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
@@ -2062,7 +2078,7 @@
     // that happens before we call setDefaultMaxBufferCount.  It's possible that the
     // driver does not dequeue a buffer at EGLSurface creation time, so we
     // cannot rely on this to cause the second dequeueBuffer call to block.
-    mST->updateTexImage();
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     mFC->waitForFrame();
     mFC->finishFrame();
@@ -2081,15 +2097,15 @@
     }
 
     // Consume the two pending buffers to unblock the producer thread.
-    mST->updateTexImage();
-    mST->updateTexImage();
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+    ASSERT_EQ(NO_ERROR, mST->updateTexImage());
 
     // Consume the remaining buffers from the producer thread.
     for (int i = 0; i < NUM_ITERATIONS-3; i++) {
         mFC->waitForFrame();
         mFC->finishFrame();
         ALOGV("+updateTexImage");
-        mST->updateTexImage();
+        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
         ALOGV("-updateTexImage");
     }
 }
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 57063e5..b9cab85 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -258,7 +258,12 @@
     mOwner = ownHandle;
 
     if (handle != 0) {
-        mBufferMapper.registerBuffer(handle);
+        status_t err = mBufferMapper.registerBuffer(handle);
+        if (err != NO_ERROR) {
+            ALOGE("unflatten: registerBuffer failed: %s (%d)",
+                    strerror(-err), err);
+            return err;
+        }
     }
 
     return NO_ERROR;
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index a3d8b01..94fb1d5 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -48,22 +48,20 @@
 
 // ----------------------------------------------------------------------------
 
-Region::Region()
-    : mBounds(0,0)
-{
+Region::Region() {
+    mStorage.add(Rect(0,0));
 }
 
 Region::Region(const Region& rhs)
-    : mBounds(rhs.mBounds), mStorage(rhs.mStorage)
+    : mStorage(rhs.mStorage)
 {
 #if VALIDATE_REGIONS
     validate(rhs, "rhs copy-ctor");
 #endif
 }
 
-Region::Region(const Rect& rhs)
-    : mBounds(rhs)
-{
+Region::Region(const Rect& rhs) {
+    mStorage.add(rhs);
 }
 
 Region::~Region()
@@ -76,40 +74,46 @@
     validate(*this, "this->operator=");
     validate(rhs, "rhs.operator=");
 #endif
-    mBounds = rhs.mBounds;
     mStorage = rhs.mStorage;
     return *this;
 }
 
 Region& Region::makeBoundsSelf()
 {
-    mStorage.clear();
+    if (mStorage.size() >= 2) {
+        const Rect bounds(getBounds());
+        mStorage.clear();
+        mStorage.add(bounds);
+    }
     return *this;
 }
 
 void Region::clear()
 {
-    mBounds.clear();
     mStorage.clear();
+    mStorage.add(Rect(0,0));
 }
 
 void Region::set(const Rect& r)
 {
-    mBounds = r;
     mStorage.clear();
+    mStorage.add(r);
 }
 
 void Region::set(uint32_t w, uint32_t h)
 {
-    mBounds = Rect(int(w), int(h));
     mStorage.clear();
+    mStorage.add(Rect(w,h));
 }
 
 // ----------------------------------------------------------------------------
 
 void Region::addRectUnchecked(int l, int t, int r, int b)
 {
-    mStorage.add(Rect(l,t,r,b));
+    Rect rect(l,t,r,b);
+    size_t where = mStorage.size() - 1;
+    mStorage.insertAt(rect, where, 1);
+
 #if VALIDATE_REGIONS
     validate(*this, "addRectUnchecked");
 #endif
@@ -252,7 +256,7 @@
 // to obtain an optimal region.
 class Region::rasterizer : public region_operator<Rect>::region_rasterizer 
 {
-    Rect& bounds;
+    Rect bounds;
     Vector<Rect>& storage;
     Rect* head;
     Rect* tail;
@@ -260,10 +264,7 @@
     Rect* cur;
 public:
     rasterizer(Region& reg) 
-        : bounds(reg.mBounds), storage(reg.mStorage), head(), tail(), cur() {
-        bounds.top = bounds.bottom = 0;
-        bounds.left   = INT_MAX;
-        bounds.right  = INT_MIN;
+        : bounds(INT_MAX, 0, INT_MIN, 0), storage(reg.mStorage), head(), tail(), cur() {
         storage.clear();
     }
 
@@ -281,6 +282,7 @@
             bounds.left  = 0;
             bounds.right = 0;
         }
+        storage.add(bounds);
     }
     
     virtual void operator()(const Rect& rect) {
@@ -372,6 +374,9 @@
                 reg.getBounds().left, reg.getBounds().top, 
                 reg.getBounds().right, reg.getBounds().bottom);
     }
+    if (reg.mStorage.size() == 2) {
+        ALOGE("mStorage size is 2, which is never valid");
+    }
     if (result == false) {
         reg.dump(name);
     }
@@ -529,11 +534,10 @@
 
 void Region::translate(Region& reg, int dx, int dy)
 {
-    if (!reg.isEmpty()) {
+    if ((dx || dy) && !reg.isEmpty()) {
 #if VALIDATE_REGIONS
         validate(reg, "translate (before)");
 #endif
-        reg.mBounds.translate(dx, dy);
         size_t count = reg.mStorage.size();
         Rect* rects = reg.mStorage.editArray();
         while (count) {
@@ -556,12 +560,11 @@
 // ----------------------------------------------------------------------------
 
 size_t Region::getSize() const {
-    return (mStorage.size() + 1) * sizeof(Rect);
+    return mStorage.size() * sizeof(Rect);
 }
 
 status_t Region::flatten(void* buffer) const {
     Rect* rects = reinterpret_cast<Rect*>(buffer);
-    *rects++ = mBounds;
     memcpy(rects, mStorage.array(), mStorage.size() * sizeof(Rect));
     return NO_ERROR;
 }
@@ -570,8 +573,6 @@
     mStorage.clear();
     if (size >= sizeof(Rect)) {
         Rect const* rects = reinterpret_cast<Rect const*>(buffer);
-        mBounds = *rects++;
-        size -= sizeof(Rect);
         size_t count = size / sizeof(Rect);
         if (count > 0) {
             ssize_t err = mStorage.insertAt(0, count);
@@ -581,25 +582,21 @@
             memcpy(mStorage.editArray(), rects, count*sizeof(Rect));
         }
     }
+#if VALIDATE_REGIONS
+    validate(*this, "Region::unflatten");
+#endif
     return NO_ERROR;
 }
 
 // ----------------------------------------------------------------------------
 
 Region::const_iterator Region::begin() const {
-    return isRect() ? &mBounds : mStorage.array();
+    return mStorage.array();
 }
 
 Region::const_iterator Region::end() const {
-    if (isRect()) {
-        if (isEmpty()) {
-            return &mBounds;
-        } else {
-            return &mBounds + 1;
-        }
-    } else {
-        return mStorage.array() + mStorage.size();
-    }
+    size_t numRects = isRect() ? 1 : mStorage.size() - 1;
+    return mStorage.array() + numRects;
 }
 
 Rect const* Region::getArray(size_t* count) const {
@@ -609,14 +606,16 @@
     return b;
 }
 
-size_t Region::getRects(Vector<Rect>& rectList) const
-{
-    rectList = mStorage;
-    if (rectList.isEmpty()) {
-        rectList.clear();
-        rectList.add(mBounds);
+SharedBuffer const* Region::getSharedBuffer(size_t* count) const {
+    // We can get to the SharedBuffer of a Vector<Rect> because Rect has
+    // a trivial destructor.
+    SharedBuffer const* sb = SharedBuffer::bufferFromData(mStorage.array());
+    if (count) {
+        size_t numRects = isRect() ? 1 : mStorage.size() - 1;
+        count[0] = numRects;
     }
-    return rectList.size();
+    sb->acquire();
+    return sb;
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp
index 020ec15..8083bba 100644
--- a/libs/utils/VectorImpl.cpp
+++ b/libs/utils/VectorImpl.cpp
@@ -51,7 +51,7 @@
         mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)
 {
     if (mStorage) {
-        SharedBuffer::sharedBuffer(mStorage)->acquire();
+        SharedBuffer::bufferFromData(mStorage)->acquire();
     }
 }
 
@@ -73,7 +73,7 @@
         if (rhs.mCount) {
             mStorage = rhs.mStorage;
             mCount = rhs.mCount;
-            SharedBuffer::sharedBuffer(mStorage)->acquire();
+            SharedBuffer::bufferFromData(mStorage)->acquire();
         } else {
             mStorage = 0;
             mCount = 0;
@@ -85,7 +85,7 @@
 void* VectorImpl::editArrayImpl()
 {
     if (mStorage) {
-        SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit();
+        SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit();
         if (sb == 0) {
             sb = SharedBuffer::alloc(capacity() * mItemSize);
             if (sb) {
@@ -101,7 +101,7 @@
 size_t VectorImpl::capacity() const
 {
     if (mStorage) {
-        return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize;
+        return SharedBuffer::bufferFromData(mStorage)->size() / mItemSize;
     }
     return 0;
 }
@@ -346,7 +346,7 @@
 void VectorImpl::release_storage()
 {
     if (mStorage) {
-        const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage);
+        const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);
         if (sb->release(SharedBuffer::eKeepStorage) == 1) {
             _do_destroy(mStorage, mCount);
             SharedBuffer::dealloc(sb);
@@ -372,7 +372,7 @@
             (mFlags & HAS_TRIVIAL_COPY) &&
             (mFlags & HAS_TRIVIAL_DTOR))
         {
-            const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
+            const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
             SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
             mStorage = sb->data();
         } else {
@@ -424,7 +424,7 @@
             (mFlags & HAS_TRIVIAL_COPY) &&
             (mFlags & HAS_TRIVIAL_DTOR))
         {
-            const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
+            const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
             SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
             mStorage = sb->data();
         } else {
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index dd0dc16..07002cc 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -52,3 +52,19 @@
 LOCAL_MODULE:= libsurfaceflinger
 
 include $(BUILD_SHARED_LIBRARY)
+
+###############################################################
+# uses jni which may not be available in PDK
+ifneq ($(wildcard libnativehelper/include),)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= \
+    DdmConnection.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libdl
+
+LOCAL_MODULE:= libsurfaceflinger_ddmconnection
+
+include $(BUILD_SHARED_LIBRARY)
+endif # libnativehelper
diff --git a/services/surfaceflinger/DdmConnection.cpp b/services/surfaceflinger/DdmConnection.cpp
index 433b38e..ece965c 100644
--- a/services/surfaceflinger/DdmConnection.cpp
+++ b/services/surfaceflinger/DdmConnection.cpp
@@ -23,6 +23,10 @@
 
 namespace android {
 
+void DdmConnection_start(const char* name) {
+    ALOGI("DdmConnection_start");
+    DdmConnection::start(name);
+}
 
 void DdmConnection::start(const char* name) {
     JavaVM* vm;
diff --git a/services/surfaceflinger/DdmConnection.h b/services/surfaceflinger/DdmConnection.h
index 91b737c..b6b088b 100644
--- a/services/surfaceflinger/DdmConnection.h
+++ b/services/surfaceflinger/DdmConnection.h
@@ -19,6 +19,9 @@
 
 namespace android {
 
+// wrapper for dlsym
+extern "C" void DdmConnection_start(const char* name);
+
 class DdmConnection {
 public:
     static void start(const char* name);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 75c228d..23290e3 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -132,23 +132,21 @@
                 mCBContext->hwc = this;
                 mCBContext->procs.invalidate = &hook_invalidate;
                 mCBContext->procs.vsync = &hook_vsync;
+                if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
+                    mCBContext->procs.hotplug = &hook_hotplug;
+                else
+                    mCBContext->procs.hotplug = NULL;
                 memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
                 mHwc->registerProcs(mHwc, &mCBContext->procs);
             }
 
             // always turn vsync off when we start
             needVSyncThread = false;
-            mHwc->eventControl(mHwc, 0, HWC_EVENT_VSYNC, 0);
-
-            int period;
-            if (mHwc->query(mHwc, HWC_VSYNC_PERIOD, &period) == NO_ERROR) {
-                mDisplayData[HWC_DISPLAY_PRIMARY].refresh = nsecs_t(period);
-            }
+            mHwc->eventControl(mHwc, HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
 
             // these IDs are always reserved
             for (size_t i=0 ; i<HWC_NUM_DISPLAY_TYPES ; i++) {
                 mAllocatedDisplayIDs.markBit(i);
-                // TODO: we query xdpi / ydpi / refresh
             }
 
             // the number of displays we actually have depends on the
@@ -166,7 +164,9 @@
     }
 
     if (fbDev) {
-        // if we're here it means we are on version 1.0
+        ALOG_ASSERT(!(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)),
+                "should only have fbdev if no hwc or hwc is 1.0");
+
         DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]);
         disp.xdpi = fbDev->xdpi;
         disp.ydpi = fbDev->ydpi;
@@ -176,8 +176,11 @@
         }
         if (disp.refresh == 0) {
             disp.refresh = nsecs_t(1e9 / 60.0);
-            ALOGW("getting VSYNC period thin air: %lld", mDisplayData[HWC_DISPLAY_PRIMARY].refresh);
+            ALOGW("getting VSYNC period from thin air: %lld",
+                    mDisplayData[HWC_DISPLAY_PRIMARY].refresh);
         }
+    } else if (mHwc) {
+        queryDisplayProperties(HWC_DISPLAY_PRIMARY);
     }
 
     if (needVSyncThread) {
@@ -207,24 +210,111 @@
     ctx->hwc->invalidate();
 }
 
-void HWComposer::hook_vsync(const struct hwc_procs* procs, int dpy,
+void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp,
         int64_t timestamp) {
     cb_context* ctx = reinterpret_cast<cb_context*>(
             const_cast<hwc_procs_t*>(procs));
-    ctx->hwc->vsync(dpy, timestamp);
+    ctx->hwc->vsync(disp, timestamp);
+}
+
+void HWComposer::hook_hotplug(const struct hwc_procs* procs, int disp,
+        int connected) {
+    cb_context* ctx = reinterpret_cast<cb_context*>(
+            const_cast<hwc_procs_t*>(procs));
+    ctx->hwc->hotplug(disp, connected);
 }
 
 void HWComposer::invalidate() {
     mFlinger->repaintEverything();
 }
 
-void HWComposer::vsync(int dpy, int64_t timestamp) {
+void HWComposer::vsync(int disp, int64_t timestamp) {
     ATRACE_INT("VSYNC", ++mVSyncCount&1);
-    mEventHandler.onVSyncReceived(dpy, timestamp);
+    mEventHandler.onVSyncReceived(disp, timestamp);
     Mutex::Autolock _l(mLock);
     mLastHwVSync = timestamp;
 }
 
+void HWComposer::hotplug(int disp, int connected) {
+    if (disp == HWC_DISPLAY_PRIMARY || disp >= HWC_NUM_DISPLAY_TYPES) {
+        ALOGE("hotplug event received for invalid display: disp=%d connected=%d",
+                disp, connected);
+        return;
+    }
+
+    if (connected)
+        queryDisplayProperties(disp);
+
+    // TODO: tell someone else about this
+}
+
+static const uint32_t DISPLAY_ATTRIBUTES[] = {
+    HWC_DISPLAY_VSYNC_PERIOD,
+    HWC_DISPLAY_RESOLUTION_X,
+    HWC_DISPLAY_RESOLUTION_Y,
+    HWC_DISPLAY_DPI_X,
+    HWC_DISPLAY_DPI_Y,
+    HWC_DISPLAY_NO_ATTRIBUTE,
+};
+#define NUM_DISPLAY_ATTRIBUTES (sizeof(DISPLAY_ATTRIBUTES) / sizeof(DISPLAY_ATTRIBUTES)[0])
+
+// http://developer.android.com/reference/android/util/DisplayMetrics.html
+#define ANDROID_DENSITY_TV    213
+#define ANDROID_DENSITY_XHIGH 320
+
+void HWComposer::queryDisplayProperties(int disp) {
+    ALOG_ASSERT(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1));
+
+    int32_t values[NUM_DISPLAY_ATTRIBUTES - 1];
+    memset(values, 0, sizeof(values));
+
+    uint32_t config;
+    size_t numConfigs = 1;
+    status_t err = mHwc->getDisplayConfigs(mHwc, disp, &config, &numConfigs);
+    if (err == NO_ERROR) {
+        mHwc->getDisplayAttributes(mHwc, disp, config, DISPLAY_ATTRIBUTES,
+                values);
+    }
+
+    int32_t w = 0, h = 0;
+    for (size_t i = 0; i < NUM_DISPLAY_ATTRIBUTES - 1; i++) {
+        switch (DISPLAY_ATTRIBUTES[i]) {
+        case HWC_DISPLAY_VSYNC_PERIOD:
+            mDisplayData[disp].refresh = nsecs_t(values[i]);
+            break;
+        case HWC_DISPLAY_RESOLUTION_X:
+            // TODO: we'll probably want to remember this eventually
+            w = values[i];
+            break;
+        case HWC_DISPLAY_RESOLUTION_Y:
+            // TODO: we'll probably want to remember this eventually
+            h = values[i];
+            break;
+        case HWC_DISPLAY_DPI_X:
+            mDisplayData[disp].xdpi = values[i] / 1000.0f;
+            break;
+        case HWC_DISPLAY_DPI_Y:
+            mDisplayData[disp].ydpi = values[i] / 1000.0f;
+            break;
+        default:
+            ALOG_ASSERT(false, "unknown display attribute %#x",
+                    DISPLAY_ATTRIBUTES[i]);
+            break;
+        }
+    }
+
+    if (mDisplayData[disp].xdpi == 0.0f || mDisplayData[disp].ydpi == 0.0f) {
+        // is there anything smarter we can do?
+        if (h >= 1080) {
+            mDisplayData[disp].xdpi = ANDROID_DENSITY_XHIGH;
+            mDisplayData[disp].ydpi = ANDROID_DENSITY_XHIGH;
+        } else {
+            mDisplayData[disp].xdpi = ANDROID_DENSITY_TV;
+            mDisplayData[disp].ydpi = ANDROID_DENSITY_TV;
+        }
+    }
+}
+
 int32_t HWComposer::allocateDisplayId() {
     if (mAllocatedDisplayIDs.count() >= mNumDisplays) {
         return NO_MEMORY;
@@ -483,9 +573,12 @@
         reinterpret_cast<Rect&>(getLayer()->sourceCrop) = crop;
     }
     virtual void setVisibleRegionScreen(const Region& reg) {
-        getLayer()->visibleRegionScreen.rects =
-                reinterpret_cast<hwc_rect_t const *>(
-                        reg.getArray(&getLayer()->visibleRegionScreen.numRects));
+        // Region::getSharedBuffer creates a reference to the underlying
+        // SharedBuffer of this Region, this reference is freed
+        // in onDisplayed()
+        hwc_region_t& visibleRegion = getLayer()->visibleRegionScreen;
+        SharedBuffer const* sb = reg.getSharedBuffer(&visibleRegion.numRects);
+        visibleRegion.rects = reinterpret_cast<hwc_rect_t const *>(sb->data());
     }
     virtual void setBuffer(const sp<GraphicBuffer>& buffer) {
         if (buffer == 0 || buffer->handle == 0) {
@@ -496,6 +589,16 @@
             getLayer()->handle = buffer->handle;
         }
     }
+    virtual void onDisplayed() {
+        hwc_region_t& visibleRegion = getLayer()->visibleRegionScreen;
+        SharedBuffer const* sb = SharedBuffer::bufferFromData(visibleRegion.rects);
+        if (sb) {
+            sb->release();
+            // not technically needed but safer
+            visibleRegion.numRects = 0;
+            visibleRegion.rects = NULL;
+        }
+    }
 };
 
 /*
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 7b92d2e..a49a023 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -53,7 +53,7 @@
 public:
     class EventHandler {
         friend class HWComposer;
-        virtual void onVSyncReceived(int dpy, nsecs_t timestamp) = 0;
+        virtual void onVSyncReceived(int disp, nsecs_t timestamp) = 0;
     protected:
         virtual ~EventHandler() {}
     };
@@ -130,6 +130,7 @@
         virtual void setVisibleRegionScreen(const Region& reg) = 0;
         virtual void setBuffer(const sp<GraphicBuffer>& buffer) = 0;
         virtual void setAcquireFenceFd(int fenceFd) = 0;
+        virtual void onDisplayed() = 0;
     };
 
     /*
@@ -242,12 +243,16 @@
     struct cb_context;
 
     static void hook_invalidate(const struct hwc_procs* procs);
-    static void hook_vsync(const struct hwc_procs* procs, int dpy,
+    static void hook_vsync(const struct hwc_procs* procs, int disp,
             int64_t timestamp);
+    static void hook_hotplug(const struct hwc_procs* procs, int disp,
+            int connected);
 
     inline void invalidate();
-    inline void vsync(int dpy, int64_t timestamp);
+    inline void vsync(int disp, int64_t timestamp);
+    inline void hotplug(int disp, int connected);
 
+    void queryDisplayProperties(int disp);
 
     struct DisplayData {
         DisplayData() : xdpi(0), ydpi(0), refresh(0),
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index ea1bc54..f928805 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -72,6 +72,7 @@
 
 void Layer::onLayerDisplayed(const sp<const DisplayDevice>& hw,
         HWComposer::HWCLayerInterface* layer) {
+    LayerBaseClient::onLayerDisplayed(hw, layer);
     if (layer) {
         mSurfaceTexture->setReleaseFence(layer->getAndResetReleaseFenceFd());
     }
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index a2513a2..87dc572 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -288,12 +288,16 @@
     // scaling is already applied in transformedBounds
     layer.setFrame(transformedBounds);
     layer.setCrop(transformedBounds.getBounds());
-    layer.setVisibleRegionScreen(tr.transform(visibleRegion));
 }
 
 void LayerBase::setPerFrameData(const sp<const DisplayDevice>& hw,
         HWComposer::HWCLayerInterface& layer) {
     layer.setBuffer(0);
+    // we have to set the visible region on every frame because
+    // we currently free it during onLayerDisplayed(), which is called
+    // after HWComposer::commit() -- every frame.
+    const Transform& tr = hw->getTransform();
+    layer.setVisibleRegionScreen(tr.transform(visibleRegion));
 }
 
 void LayerBase::setAcquireFence(const sp<const DisplayDevice>& hw,
@@ -301,6 +305,13 @@
     layer.setAcquireFenceFd(-1);
 }
 
+void LayerBase::onLayerDisplayed(const sp<const DisplayDevice>& hw,
+        HWComposer::HWCLayerInterface* layer) {
+    if (layer) {
+        layer->onDisplayed();
+    }
+}
+
 void LayerBase::setFiltering(bool filtering)
 {
     mFiltering = filtering;
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index c18f397..4651517 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -218,7 +218,7 @@
     /** called after page-flip
      */
     virtual void onLayerDisplayed(const sp<const DisplayDevice>& hw,
-            HWComposer::HWCLayerInterface* layer) { }
+            HWComposer::HWCLayerInterface* layer);
 
     /** called before composition.
      * returns true if the layer has pending updates.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index cd1b336..ee653f3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <errno.h>
 #include <math.h>
+#include <dlfcn.h>
 
 #include <EGL/egl.h>
 #include <GLES/gl.h>
@@ -108,9 +109,11 @@
     property_get("debug.sf.ddms", value, "0");
     mDebugDDMS = atoi(value);
     if (mDebugDDMS) {
-        DdmConnection::start(getServiceName());
+        if (!startDdmConnection()) {
+            // start failed, and DDMS debugging not enabled
+            mDebugDDMS = 0;
+        }
     }
-
     ALOGI_IF(mDebugRegion, "showupdates enabled");
     ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
 }
@@ -1255,8 +1258,9 @@
     // FIXME: we need to call eglSwapBuffers() on displays that have
     // GL composition and only on those.
     // however, currently hwc.commit() already does that for the main
-    // display and never for the other ones
-    if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) {
+    // display (if there is a hwc) and never for the other ones
+    if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL ||
+            getHwComposer().initCheck() != NO_ERROR) {
         // FIXME: EGL spec says:
         //   "surface must be bound to the calling thread's current context,
         //    for the current rendering API."
@@ -2060,6 +2064,24 @@
     hw->dump(result);
 }
 
+bool SurfaceFlinger::startDdmConnection()
+{
+    void* libddmconnection_dso =
+            dlopen("libsurfaceflinger_ddmconnection.so", RTLD_NOW);
+    if (!libddmconnection_dso) {
+        return false;
+    }
+    void (*DdmConnection_start)(const char* name);
+    DdmConnection_start =
+            (typeof DdmConnection_start)dlsym(libddmconnection_dso, "DdmConnection_start");
+    if (!DdmConnection_start) {
+        dlclose(libddmconnection_dso);
+        return false;
+    }
+    (*DdmConnection_start)(getServiceName());
+    return true;
+}
+
 status_t SurfaceFlinger::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 50fef00..9db6b2d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -388,6 +388,7 @@
     void clearStatsLocked(const Vector<String16>& args, size_t& index,
         String8& result, char* buffer, size_t SIZE) const;
     void dumpAllLocked(String8& result, char* buffer, size_t SIZE) const;
+    bool startDdmConnection();
 
     /* ------------------------------------------------------------------------
      * Attributes