Merge "Check orientation range has been initialized first"
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index cc41bae..000ef0e 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -21,6 +21,7 @@
 #include <EGL/eglext.h>
 
 #include <ui/Rect.h>
+#include <ui/Region.h>
 
 #include <system/graphics.h>
 
@@ -106,6 +107,10 @@
     // Indicates this buffer must be transformed by the inverse transform of the screen
     // it is displayed onto. This is applied after mTransform.
     bool mTransformToDisplayInverse;
+
+    // Describes the portion of the surface that has been modified since the
+    // previous frame
+    Region mSurfaceDamage;
 };
 
 } // namespace android
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index 40026bd..9a43516 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -30,6 +30,9 @@
 #include <utils/Trace.h>
 #include <utils/Vector.h>
 
+#include <list>
+#include <set>
+
 #define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
 #define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
 #define BQ_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
@@ -123,6 +126,10 @@
     // waitWhileAllocatingLocked blocks until mIsAllocating is false.
     void waitWhileAllocatingLocked() const;
 
+    // validateConsistencyLocked ensures that the free lists are in sync with
+    // the information stored in mSlots
+    void validateConsistencyLocked() const;
+
     // mAllocator is the connection to SurfaceFlinger that is used to allocate
     // new GraphicBuffer objects.
     sp<IGraphicBufferAlloc> mAllocator;
@@ -177,6 +184,14 @@
     // mQueue is a FIFO of queued buffers used in synchronous mode.
     Fifo mQueue;
 
+    // mFreeSlots contains all of the slots which are FREE and do not currently
+    // have a buffer attached
+    std::set<int> mFreeSlots;
+
+    // mFreeBuffers contains all of the slots which are FREE and currently have
+    // a buffer attached
+    std::list<int> mFreeBuffers;
+
     // mOverrideMaxBufferCount is the limit on the number of buffers that will
     // be allocated at one time. This value is set by the producer by calling
     // setBufferCount. The default is 0, which means that the producer doesn't
@@ -251,6 +266,10 @@
     // mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating
     // becomes false.
     mutable Condition mIsAllocatingCondition;
+
+    // mAllowAllocation determines whether dequeueBuffer is allowed to allocate
+    // new buffers
+    bool mAllowAllocation;
 }; // class BufferQueueCore
 
 } // namespace android
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index f794ea3..ed660fb 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -172,6 +172,9 @@
     virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
             PixelFormat format, uint32_t usage);
 
+    // See IGraphicBufferProducer::allowAllocation
+    virtual status_t allowAllocation(bool allow);
+
 private:
     // This is required by the IBinder::DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 374245a..5c50b2b 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -28,6 +28,7 @@
 #include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
+#include <ui/Region.h>
 
 namespace android {
 // ----------------------------------------------------------------------------
@@ -281,7 +282,7 @@
                 : timestamp(timestamp), isAutoTimestamp(isAutoTimestamp),
                   dataSpace(dataSpace), crop(crop), scalingMode(scalingMode),
                   transform(transform), stickyTransform(sticky),
-                  async(async), fence(fence) { }
+                  async(async), fence(fence), surfaceDamage() { }
         inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
                 android_dataspace* outDataSpace,
                 Rect* outCrop, int* outScalingMode,
@@ -306,6 +307,9 @@
         status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
         status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
 
+        const Region& getSurfaceDamage() const { return surfaceDamage; }
+        void setSurfaceDamage(const Region& damage) { surfaceDamage = damage; }
+
     private:
         int64_t timestamp;
         int isAutoTimestamp;
@@ -316,6 +320,7 @@
         uint32_t stickyTransform;
         int async;
         sp<Fence> fence;
+        Region surfaceDamage;
     };
 
     // QueueBufferOutput must be a POD structure
@@ -453,6 +458,18 @@
     // allocated, this function has no effect.
     virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
             PixelFormat format, uint32_t usage) = 0;
+
+    // Sets whether dequeueBuffer is allowed to allocate new buffers.
+    //
+    // Normally dequeueBuffer does not discriminate between free slots which
+    // already have an allocated buffer and those which do not, and will
+    // allocate a new buffer if the slot doesn't have a buffer or if the slot's
+    // buffer doesn't match the requested size, format, or usage. This method
+    // allows the producer to restrict the eligible slots to those which already
+    // have an allocated buffer of the correct size, format, and usage. If no
+    // eligible slot is available, dequeueBuffer will block or return an error
+    // as usual.
+    virtual status_t allowAllocation(bool allow) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index e973483..a9f78cf 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -147,6 +147,7 @@
     int dispatchUnlockAndPost(va_list args);
     int dispatchSetSidebandStream(va_list args);
     int dispatchSetBuffersDataSpace(va_list args);
+    int dispatchSetSurfaceDamage(va_list args);
 
 protected:
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -171,6 +172,7 @@
     virtual int setBuffersDataSpace(android_dataspace dataSpace);
     virtual int setCrop(Rect const* rect);
     virtual int setUsage(uint32_t reqUsage);
+    virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
 
 public:
     virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
@@ -296,7 +298,12 @@
     sp<GraphicBuffer>           mPostedBuffer;
     bool                        mConnectedToCpu;
 
-    // must be accessed from lock/unlock thread only
+    // When a CPU producer is attached, this reflects the region that the
+    // producer wished to update as well as whether the Surface was able to copy
+    // the previous buffer back to allow a partial update.
+    //
+    // When a non-CPU producer is attached, this reflects the surface damage
+    // (the change since the previous frame) passed in by the producer.
     Region mDirtyRegion;
 };
 
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index cea94fc..f91d192 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -97,6 +97,9 @@
     status_t reallocate(uint32_t inWidth, uint32_t inHeight,
             PixelFormat inFormat, uint32_t inUsage);
 
+    bool needsReallocation(uint32_t inWidth, uint32_t inHeight,
+            PixelFormat inFormat, uint32_t inUsage);
+
     status_t lock(uint32_t inUsage, void** vaddr);
     status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr);
     // For HAL_PIXEL_FORMAT_YCbCr_420_888
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index 40d1166..3886f93 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -31,6 +31,8 @@
 public:
     typedef ARect::value_type value_type;
 
+    static const Rect INVALID_RECT;
+
     // we don't provide copy-ctor and operator= on purpose
     // because we want the compiler generated versions
 
diff --git a/include/ui/Region.h b/include/ui/Region.h
index 49740f7..2a14918 100644
--- a/include/ui/Region.h
+++ b/include/ui/Region.h
@@ -35,6 +35,8 @@
 class Region : public LightFlattenable<Region>
 {
 public:
+    static const Region INVALID_REGION;
+
                         Region();
                         Region(const Region& rhs);
     explicit            Region(const Rect& rhs);
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index 312fb3b..5793d40 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -39,50 +39,66 @@
 
 BufferItem::~BufferItem() {}
 
+template <typename T>
+static void addAligned(size_t& size, T /* value */) {
+    size = FlattenableUtils::align<sizeof(T)>(size);
+    size += sizeof(T);
+}
+
 size_t BufferItem::getPodSize() const {
-    size_t c =  sizeof(mCrop) +
-            sizeof(mTransform) +
-            sizeof(mScalingMode) +
-            sizeof(mTimestamp) +
-            sizeof(mIsAutoTimestamp) +
-            sizeof(mDataSpace) +
-            sizeof(mFrameNumber) +
-            sizeof(mSlot) +
-            sizeof(mIsDroppable) +
-            sizeof(mAcquireCalled) +
-            sizeof(mTransformToDisplayInverse);
-    return c;
+    // Must align<8> before writing these fields for this to be correct
+    size_t size = 0;
+    addAligned(size, mCrop);
+    addAligned(size, mTransform);
+    addAligned(size, mScalingMode);
+    addAligned(size, mTimestamp);
+    addAligned(size, mIsAutoTimestamp);
+    addAligned(size, mDataSpace);
+    addAligned(size, mFrameNumber);
+    addAligned(size, mSlot);
+    addAligned(size, mIsDroppable);
+    addAligned(size, mAcquireCalled);
+    addAligned(size, mTransformToDisplayInverse);
+    return size;
 }
 
 size_t BufferItem::getFlattenedSize() const {
-    size_t c = 0;
+    size_t size = sizeof(uint32_t); // Flags
     if (mGraphicBuffer != 0) {
-        c += mGraphicBuffer->getFlattenedSize();
-        FlattenableUtils::align<4>(c);
+        size += mGraphicBuffer->getFlattenedSize();
+        FlattenableUtils::align<4>(size);
     }
     if (mFence != 0) {
-        c += mFence->getFlattenedSize();
-        FlattenableUtils::align<4>(c);
+        size += mFence->getFlattenedSize();
+        FlattenableUtils::align<4>(size);
     }
-    return sizeof(int32_t) + c + getPodSize();
+    size += mSurfaceDamage.getFlattenedSize();
+    size = FlattenableUtils::align<8>(size);
+    return size + getPodSize();
 }
 
 size_t BufferItem::getFdCount() const {
-    size_t c = 0;
+    size_t count = 0;
     if (mGraphicBuffer != 0) {
-        c += mGraphicBuffer->getFdCount();
+        count += mGraphicBuffer->getFdCount();
     }
     if (mFence != 0) {
-        c += mFence->getFdCount();
+        count += mFence->getFdCount();
     }
-    return c;
+    return count;
+}
+
+template <typename T>
+static void writeAligned(void*& buffer, size_t& size, T value) {
+    size -= FlattenableUtils::align<alignof(T)>(buffer);
+    FlattenableUtils::write(buffer, size, value);
 }
 
 status_t BufferItem::flatten(
         void*& buffer, size_t& size, int*& fds, size_t& count) const {
 
     // make sure we have enough space
-    if (count < BufferItem::getFlattenedSize()) {
+    if (size < BufferItem::getFlattenedSize()) {
         return NO_MEMORY;
     }
 
@@ -106,31 +122,45 @@
         flags |= 2;
     }
 
-    // check we have enough space (in case flattening the fence/graphicbuffer lied to us)
+    status_t err = mSurfaceDamage.flatten(buffer, size);
+    if (err) return err;
+    FlattenableUtils::advance(buffer, size, mSurfaceDamage.getFlattenedSize());
+
+    // Must align<8> so that getPodSize returns the correct value
+    size -= FlattenableUtils::align<8>(buffer);
+
+    // Check we still have enough space
     if (size < getPodSize()) {
         return NO_MEMORY;
     }
 
-    FlattenableUtils::write(buffer, size, mCrop);
-    FlattenableUtils::write(buffer, size, mTransform);
-    FlattenableUtils::write(buffer, size, mScalingMode);
-    FlattenableUtils::write(buffer, size, mTimestamp);
-    FlattenableUtils::write(buffer, size, mIsAutoTimestamp);
-    FlattenableUtils::write(buffer, size, mDataSpace);
-    FlattenableUtils::write(buffer, size, mFrameNumber);
-    FlattenableUtils::write(buffer, size, mSlot);
-    FlattenableUtils::write(buffer, size, mIsDroppable);
-    FlattenableUtils::write(buffer, size, mAcquireCalled);
-    FlattenableUtils::write(buffer, size, mTransformToDisplayInverse);
+    writeAligned(buffer, size, mCrop);
+    writeAligned(buffer, size, mTransform);
+    writeAligned(buffer, size, mScalingMode);
+    writeAligned(buffer, size, mTimestamp);
+    writeAligned(buffer, size, mIsAutoTimestamp);
+    writeAligned(buffer, size, mDataSpace);
+    writeAligned(buffer, size, mFrameNumber);
+    writeAligned(buffer, size, mSlot);
+    writeAligned(buffer, size, mIsDroppable);
+    writeAligned(buffer, size, mAcquireCalled);
+    writeAligned(buffer, size, mTransformToDisplayInverse);
 
     return NO_ERROR;
 }
 
+template <typename T>
+static void readAligned(const void*& buffer, size_t& size, T& value) {
+    size -= FlattenableUtils::align<alignof(T)>(buffer);
+    FlattenableUtils::read(buffer, size, value);
+}
+
 status_t BufferItem::unflatten(
         void const*& buffer, size_t& size, int const*& fds, size_t& count) {
 
-    if (size < sizeof(uint32_t))
+    if (size < sizeof(uint32_t)) {
         return NO_MEMORY;
+    }
 
     uint32_t flags = 0;
     FlattenableUtils::read(buffer, size, flags);
@@ -149,22 +179,29 @@
         size -= FlattenableUtils::align<4>(buffer);
     }
 
-    // check we have enough space
+    status_t err = mSurfaceDamage.unflatten(buffer, size);
+    if (err) return err;
+    FlattenableUtils::advance(buffer, size, mSurfaceDamage.getFlattenedSize());
+
+    // Must align<8> so that getPodSize returns the correct value
+    size -= FlattenableUtils::align<8>(buffer);
+
+    // Check we still have enough space
     if (size < getPodSize()) {
         return NO_MEMORY;
     }
 
-    FlattenableUtils::read(buffer, size, mCrop);
-    FlattenableUtils::read(buffer, size, mTransform);
-    FlattenableUtils::read(buffer, size, mScalingMode);
-    FlattenableUtils::read(buffer, size, mTimestamp);
-    FlattenableUtils::read(buffer, size, mIsAutoTimestamp);
-    FlattenableUtils::read(buffer, size, mDataSpace);
-    FlattenableUtils::read(buffer, size, mFrameNumber);
-    FlattenableUtils::read(buffer, size, mSlot);
-    FlattenableUtils::read(buffer, size, mIsDroppable);
-    FlattenableUtils::read(buffer, size, mAcquireCalled);
-    FlattenableUtils::read(buffer, size, mTransformToDisplayInverse);
+    readAligned(buffer, size, mCrop);
+    readAligned(buffer, size, mTransform);
+    readAligned(buffer, size, mScalingMode);
+    readAligned(buffer, size, mTimestamp);
+    readAligned(buffer, size, mIsAutoTimestamp);
+    readAligned(buffer, size, mDataSpace);
+    readAligned(buffer, size, mFrameNumber);
+    readAligned(buffer, size, mSlot);
+    readAligned(buffer, size, mIsDroppable);
+    readAligned(buffer, size, mAcquireCalled);
+    readAligned(buffer, size, mTransformToDisplayInverse);
 
     return NO_ERROR;
 }
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 526c3b7..c7d5e00 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -120,6 +120,7 @@
             if (mCore->stillTracking(front)) {
                 // Front buffer is still in mSlots, so mark the slot as free
                 mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
+                mCore->mFreeBuffers.push_back(front->mSlot);
             }
             mCore->mQueue.erase(front);
             front = mCore->mQueue.begin();
@@ -173,6 +174,8 @@
 
     ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
 
+    mCore->validateConsistencyLocked();
+
     return NO_ERROR;
 }
 
@@ -199,6 +202,7 @@
 
     mCore->freeBufferLocked(slot);
     mCore->mDequeueCondition.broadcast();
+    mCore->validateConsistencyLocked();
 
     return NO_ERROR;
 }
@@ -217,18 +221,11 @@
 
     Mutex::Autolock lock(mCore->mMutex);
 
-    // Make sure we don't have too many acquired buffers and find a free slot
-    // to put the buffer into (the oldest if there are multiple).
+    // Make sure we don't have too many acquired buffers
     int numAcquiredBuffers = 0;
-    int found = BufferQueueCore::INVALID_BUFFER_SLOT;
     for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
         if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
             ++numAcquiredBuffers;
-        } else if (mSlots[s].mBufferState == BufferSlot::FREE) {
-            if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
-                    mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
-                found = s;
-            }
         }
     }
 
@@ -238,6 +235,17 @@
                 mCore->mMaxAcquiredBufferCount);
         return INVALID_OPERATION;
     }
+
+    // Find a free slot to put the buffer into
+    int found = BufferQueueCore::INVALID_BUFFER_SLOT;
+    if (!mCore->mFreeSlots.empty()) {
+        auto slot = mCore->mFreeSlots.begin();
+        found = *slot;
+        mCore->mFreeSlots.erase(slot);
+    } else if (!mCore->mFreeBuffers.empty()) {
+        found = mCore->mFreeBuffers.front();
+        mCore->mFreeBuffers.remove(found);
+    }
     if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
         BQ_LOGE("attachBuffer(P): could not find free buffer slot");
         return NO_MEMORY;
@@ -271,6 +279,8 @@
     // for attached buffers.
     mSlots[*outSlot].mAcquireCalled = false;
 
+    mCore->validateConsistencyLocked();
+
     return NO_ERROR;
 }
 
@@ -311,6 +321,7 @@
             mSlots[slot].mEglFence = eglFence;
             mSlots[slot].mFence = releaseFence;
             mSlots[slot].mBufferState = BufferSlot::FREE;
+            mCore->mFreeBuffers.push_back(slot);
             listener = mCore->mConnectedProducerListener;
             BQ_LOGV("releaseBuffer: releasing slot %d", slot);
         } else if (mSlots[slot].mNeedsCleanupOnRelease) {
@@ -325,6 +336,7 @@
         }
 
         mCore->mDequeueCondition.broadcast();
+        mCore->validateConsistencyLocked();
     } // Autolock scope
 
     // Call back without lock held
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index edebc45..bc75ca7 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -53,6 +53,8 @@
     mConnectedProducerListener(),
     mSlots(),
     mQueue(),
+    mFreeSlots(),
+    mFreeBuffers(),
     mOverrideMaxBufferCount(0),
     mDequeueCondition(),
     mUseAsyncBuffer(true),
@@ -67,7 +69,8 @@
     mFrameCounter(0),
     mTransformHint(0),
     mIsAllocating(false),
-    mIsAllocatingCondition()
+    mIsAllocatingCondition(),
+    mAllowAllocation(true)
 {
     if (allocator == NULL) {
         sp<ISurfaceComposer> composer(ComposerService::getComposerService());
@@ -76,6 +79,9 @@
             BQ_LOGE("createGraphicBufferAlloc failed");
         }
     }
+    for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+        mFreeSlots.insert(slot);
+    }
 }
 
 BufferQueueCore::~BufferQueueCore() {}
@@ -190,12 +196,20 @@
 
 void BufferQueueCore::freeBufferLocked(int slot) {
     BQ_LOGV("freeBufferLocked: slot %d", slot);
+    bool hadBuffer = mSlots[slot].mGraphicBuffer != NULL;
     mSlots[slot].mGraphicBuffer.clear();
     if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
         mSlots[slot].mNeedsCleanupOnRelease = true;
     }
+    if (mSlots[slot].mBufferState != BufferSlot::FREE) {
+        mFreeSlots.insert(slot);
+    } else if (hadBuffer) {
+        // If the slot was FREE, but we had a buffer, we need to move this slot
+        // from the free buffers list to the the free slots list
+        mFreeBuffers.remove(slot);
+        mFreeSlots.insert(slot);
+    }
     mSlots[slot].mBufferState = BufferSlot::FREE;
-    mSlots[slot].mFrameNumber = UINT32_MAX;
     mSlots[slot].mAcquireCalled = false;
 
     // Destroy fence as BufferQueue now takes ownership
@@ -204,6 +218,7 @@
         mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
     }
     mSlots[slot].mFence = Fence::NO_FENCE;
+    validateConsistencyLocked();
 }
 
 void BufferQueueCore::freeAllBuffersLocked() {
@@ -236,4 +251,48 @@
     }
 }
 
+void BufferQueueCore::validateConsistencyLocked() const {
+    static const useconds_t PAUSE_TIME = 0;
+    for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+        bool isInFreeSlots = mFreeSlots.count(slot) != 0;
+        bool isInFreeBuffers =
+                std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) !=
+                mFreeBuffers.cend();
+        if (mSlots[slot].mBufferState == BufferSlot::FREE) {
+            if (mSlots[slot].mGraphicBuffer == NULL) {
+                if (!isInFreeSlots) {
+                    BQ_LOGE("Slot %d is FREE but is not in mFreeSlots", slot);
+                    usleep(PAUSE_TIME);
+                }
+                if (isInFreeBuffers) {
+                    BQ_LOGE("Slot %d is in mFreeSlots "
+                            "but is also in mFreeBuffers", slot);
+                    usleep(PAUSE_TIME);
+                }
+            } else {
+                if (!isInFreeBuffers) {
+                    BQ_LOGE("Slot %d is FREE but is not in mFreeBuffers", slot);
+                    usleep(PAUSE_TIME);
+                }
+                if (isInFreeSlots) {
+                    BQ_LOGE("Slot %d is in mFreeBuffers "
+                            "but is also in mFreeSlots", slot);
+                    usleep(PAUSE_TIME);
+                }
+            }
+        } else {
+            if (isInFreeSlots) {
+                BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%d)",
+                        slot, mSlots[slot].mBufferState);
+                usleep(PAUSE_TIME);
+            }
+            if (isInFreeBuffers) {
+                BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%d)",
+                        slot, mSlots[slot].mBufferState);
+                usleep(PAUSE_TIME);
+            }
+        }
+    }
+}
+
 } // namespace android
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 4c22ba3..86e45c8 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -161,8 +161,6 @@
             }
         }
 
-        // Look for a free buffer to give to the client
-        *found = BufferQueueCore::INVALID_BUFFER_SLOT;
         int dequeuedCount = 0;
         int acquiredCount = 0;
         for (int s = 0; s < maxBufferCount; ++s) {
@@ -173,15 +171,6 @@
                 case BufferSlot::ACQUIRED:
                     ++acquiredCount;
                     break;
-                case BufferSlot::FREE:
-                    // We return the oldest of the free buffers to avoid
-                    // stalling the producer if possible, since the consumer
-                    // may still have pending reads of in-flight buffers
-                    if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
-                            mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) {
-                        *found = s;
-                    }
-                    break;
                 default:
                     break;
             }
@@ -214,6 +203,8 @@
             }
         }
 
+        *found = BufferQueueCore::INVALID_BUFFER_SLOT;
+
         // If we disconnect and reconnect quickly, we can be in a state where
         // our slots are empty but we have many buffers in the queue. This can
         // cause us to run out of memory if we outrun the consumer. Wait here if
@@ -223,6 +214,19 @@
         if (tooManyBuffers) {
             BQ_LOGV("%s: queue size is %zu, waiting", caller,
                     mCore->mQueue.size());
+        } else {
+            if (!mCore->mFreeBuffers.empty()) {
+                auto slot = mCore->mFreeBuffers.begin();
+                *found = *slot;
+                mCore->mFreeBuffers.erase(slot);
+            } else if (mCore->mAllowAllocation && !mCore->mFreeSlots.empty()) {
+                auto slot = mCore->mFreeSlots.begin();
+                // Only return free slots up to the max buffer count
+                if (*slot < maxBufferCount) {
+                    *found = *slot;
+                    mCore->mFreeSlots.erase(slot);
+                }
+            }
         }
 
         // If no buffer is found, or if the queue has too many buffers
@@ -281,17 +285,39 @@
         // Enable the usage bits the consumer requested
         usage |= mCore->mConsumerUsageBits;
 
-        int found;
-        status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
-                &found, &returnFlags);
-        if (status != NO_ERROR) {
-            return status;
+        const bool useDefaultSize = !width && !height;
+        if (useDefaultSize) {
+            width = mCore->mDefaultWidth;
+            height = mCore->mDefaultHeight;
         }
 
-        // This should not happen
-        if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
-            BQ_LOGE("dequeueBuffer: no available buffer slots");
-            return -EBUSY;
+        int found = BufferItem::INVALID_BUFFER_SLOT;
+        while (found == BufferItem::INVALID_BUFFER_SLOT) {
+            status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
+                    &found, &returnFlags);
+            if (status != NO_ERROR) {
+                return status;
+            }
+
+            // This should not happen
+            if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+                BQ_LOGE("dequeueBuffer: no available buffer slots");
+                return -EBUSY;
+            }
+
+            const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
+
+            // If we are not allowed to allocate new buffers,
+            // waitForFreeSlotThenRelock must have returned a slot containing a
+            // buffer. If this buffer would require reallocation to meet the
+            // requested attributes, we free it and attempt to get another one.
+            if (!mCore->mAllowAllocation) {
+                if (buffer->needsReallocation(width, height, format, usage)) {
+                    mCore->freeBufferLocked(found);
+                    found = BufferItem::INVALID_BUFFER_SLOT;
+                    continue;
+                }
+            }
         }
 
         *outSlot = found;
@@ -299,20 +325,11 @@
 
         attachedByConsumer = mSlots[found].mAttachedByConsumer;
 
-        const bool useDefaultSize = !width && !height;
-        if (useDefaultSize) {
-            width = mCore->mDefaultWidth;
-            height = mCore->mDefaultHeight;
-        }
-
         mSlots[found].mBufferState = BufferSlot::DEQUEUED;
 
         const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
         if ((buffer == NULL) ||
-                (static_cast<uint32_t>(buffer->width) != width) ||
-                (static_cast<uint32_t>(buffer->height) != height) ||
-                (buffer->format != format) ||
-                ((static_cast<uint32_t>(buffer->usage) & usage) != usage))
+                buffer->needsReallocation(width, height, format, usage))
         {
             mSlots[found].mAcquireCalled = false;
             mSlots[found].mGraphicBuffer = NULL;
@@ -335,6 +352,8 @@
         *outFence = mSlots[found].mFence;
         mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
         mSlots[found].mFence = Fence::NO_FENCE;
+
+        mCore->validateConsistencyLocked();
     } // Autolock scope
 
     if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
@@ -355,7 +374,6 @@
                 return NO_INIT;
             }
 
-            mSlots[*outSlot].mFrameNumber = UINT32_MAX;
             mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
         } // Autolock scope
     }
@@ -414,6 +432,7 @@
 
     mCore->freeBufferLocked(slot);
     mCore->mDequeueCondition.broadcast();
+    mCore->validateConsistencyLocked();
 
     return NO_ERROR;
 }
@@ -438,27 +457,19 @@
         return NO_INIT;
     }
 
-    // Find the oldest valid slot
-    int found = BufferQueueCore::INVALID_BUFFER_SLOT;
-    for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-        if (mSlots[s].mBufferState == BufferSlot::FREE &&
-                mSlots[s].mGraphicBuffer != NULL) {
-            if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
-                    mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
-                found = s;
-            }
-        }
-    }
-
-    if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+    if (mCore->mFreeBuffers.empty()) {
         return NO_MEMORY;
     }
 
+    int found = mCore->mFreeBuffers.front();
+    mCore->mFreeBuffers.remove(found);
+
     BQ_LOGV("detachNextBuffer detached slot %d", found);
 
     *outBuffer = mSlots[found].mGraphicBuffer;
     *outFence = mSlots[found].mFence;
     mCore->freeBufferLocked(found);
+    mCore->validateConsistencyLocked();
 
     return NO_ERROR;
 }
@@ -506,6 +517,8 @@
     mSlots[*outSlot].mFence = Fence::NO_FENCE;
     mSlots[*outSlot].mRequestBufferCalled = true;
 
+    mCore->validateConsistencyLocked();
+
     return returnFlags;
 }
 
@@ -525,6 +538,7 @@
     sp<Fence> fence;
     input.deflate(&timestamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode,
             &transform, &async, &fence, &stickyTransform);
+    Region surfaceDamage = input.getSurfaceDamage();
 
     if (fence == NULL) {
         BQ_LOGE("queueBuffer: fence is NULL");
@@ -621,6 +635,7 @@
         item.mSlot = slot;
         item.mFence = fence;
         item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async;
+        item.mSurfaceDamage = surfaceDamage;
 
         mStickyTransform = stickyTransform;
 
@@ -638,9 +653,7 @@
                 // mark it as freed
                 if (mCore->stillTracking(front)) {
                     mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
-                    // Reset the frame number of the freed buffer so that it is
-                    // the first in line to be dequeued again
-                    mSlots[front->mSlot].mFrameNumber = 0;
+                    mCore->mFreeBuffers.push_front(front->mSlot);
                 }
                 // Overwrite the droppable buffer with the incoming one
                 *front = item;
@@ -662,6 +675,8 @@
 
         // Take a ticket for the callback functions
         callbackTicket = mNextCallbackTicket++;
+
+        mCore->validateConsistencyLocked();
     } // Autolock scope
 
     // Wait without lock held
@@ -722,10 +737,11 @@
         return;
     }
 
+    mCore->mFreeBuffers.push_front(slot);
     mSlots[slot].mBufferState = BufferSlot::FREE;
-    mSlots[slot].mFrameNumber = 0;
     mSlots[slot].mFence = fence;
     mCore->mDequeueCondition.broadcast();
+    mCore->validateConsistencyLocked();
 }
 
 int BufferQueueProducer::query(int what, int *outValue) {
@@ -930,6 +946,12 @@
             Mutex::Autolock lock(mCore->mMutex);
             mCore->waitWhileAllocatingLocked();
 
+            if (!mCore->mAllowAllocation) {
+                BQ_LOGE("allocateBuffers: allocation is not allowed for this "
+                        "BufferQueue");
+                return;
+            }
+
             int currentBufferCount = 0;
             for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
                 if (mSlots[slot].mGraphicBuffer != NULL) {
@@ -1007,17 +1029,32 @@
                 }
                 mCore->freeBufferLocked(slot); // Clean up the slot first
                 mSlots[slot].mGraphicBuffer = buffers[i];
-                mSlots[slot].mFrameNumber = 0;
                 mSlots[slot].mFence = Fence::NO_FENCE;
+
+                // freeBufferLocked puts this slot on the free slots list. Since
+                // we then attached a buffer, move the slot to free buffer list.
+                mCore->mFreeSlots.erase(slot);
+                mCore->mFreeBuffers.push_front(slot);
+
                 BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
             }
 
             mCore->mIsAllocating = false;
             mCore->mIsAllocatingCondition.broadcast();
+            mCore->validateConsistencyLocked();
         } // Autolock scope
     }
 }
 
+status_t BufferQueueProducer::allowAllocation(bool allow) {
+    ATRACE_CALL();
+    BQ_LOGV("allowAllocation: %s", allow ? "true" : "false");
+
+    Mutex::Autolock lock(mCore->mMutex);
+    mCore->mAllowAllocation = allow;
+    return NO_ERROR;
+}
+
 void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
     // If we're here, it means that a producer we were connected to died.
     // We're guaranteed that we are still connected to it because we remove
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index a3e6fb2..7093ffa 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -46,6 +46,7 @@
     DISCONNECT,
     SET_SIDEBAND_STREAM,
     ALLOCATE_BUFFERS,
+    ALLOW_ALLOCATION,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -271,6 +272,18 @@
             ALOGE("allocateBuffers failed to transact: %d", result);
         }
     }
+
+    virtual status_t allowAllocation(bool allow) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeInt32(static_cast<int32_t>(allow));
+        status_t result = remote()->transact(ALLOW_ALLOCATION, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readInt32();
+        return result;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -418,7 +431,7 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
-        case ALLOCATE_BUFFERS:
+        case ALLOCATE_BUFFERS: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             bool async = static_cast<bool>(data.readInt32());
             uint32_t width = data.readUint32();
@@ -427,6 +440,14 @@
             uint32_t usage = data.readUint32();
             allocateBuffers(async, width, height, format, usage);
             return NO_ERROR;
+        }
+        case ALLOW_ALLOCATION: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            bool allow = static_cast<bool>(data.readInt32());
+            status_t result = allowAllocation(allow);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
@@ -446,7 +467,8 @@
          + sizeof(transform)
          + sizeof(stickyTransform)
          + sizeof(async)
-         + fence->getFlattenedSize();
+         + fence->getFlattenedSize()
+         + surfaceDamage.getFlattenedSize();
 }
 
 size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
@@ -467,7 +489,11 @@
     FlattenableUtils::write(buffer, size, transform);
     FlattenableUtils::write(buffer, size, stickyTransform);
     FlattenableUtils::write(buffer, size, async);
-    return fence->flatten(buffer, size, fds, count);
+    status_t result = fence->flatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    return surfaceDamage.flatten(buffer, size);
 }
 
 status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
@@ -497,7 +523,11 @@
     FlattenableUtils::read(buffer, size, async);
 
     fence = new Fence();
-    return fence->unflatten(buffer, size, fds, count);
+    status_t result = fence->unflatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    return surfaceDamage.unflatten(buffer, size);
 }
 
 }; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index b80890f..b8acad2 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -27,6 +27,7 @@
 #include <utils/NativeHandle.h>
 
 #include <ui/Fence.h>
+#include <ui/Region.h>
 
 #include <gui/IProducerListener.h>
 #include <gui/ISurfaceComposer.h>
@@ -320,6 +321,25 @@
     IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
             mDataSpace, crop, mScalingMode, mTransform ^ mStickyTransform,
             mSwapIntervalZero, fence, mStickyTransform);
+
+    if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
+        input.setSurfaceDamage(Region::INVALID_REGION);
+    } else {
+        // The surface damage was specified using the OpenGL ES convention of
+        // the origin being in the bottom-left corner. Here we flip to the
+        // convention that the rest of the system uses (top-left corner) by
+        // subtracting all top/bottom coordinates from the buffer height.
+        Region flippedRegion;
+        for (auto rect : mDirtyRegion) {
+            auto top = buffer->height - rect.bottom;
+            auto bottom = buffer->height - rect.top;
+            Rect flippedRect{rect.left, top, rect.right, bottom};
+            flippedRegion.orSelf(flippedRect);
+        }
+
+        input.setSurfaceDamage(flippedRegion);
+    }
+
     status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
     if (err != OK)  {
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
@@ -336,6 +356,11 @@
 
     mConsumerRunningBehind = (numPendingBuffers >= 2);
 
+    if (!mConnectedToCpu) {
+        // Clear surface damage back to full-buffer
+        mDirtyRegion = Region::INVALID_REGION;
+    }
+
     return err;
 }
 
@@ -453,6 +478,9 @@
     case NATIVE_WINDOW_SET_BUFFERS_DATASPACE:
         res = dispatchSetBuffersDataSpace(args);
         break;
+    case NATIVE_WINDOW_SET_SURFACE_DAMAGE:
+        res = dispatchSetSurfaceDamage(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -556,6 +584,13 @@
     return setBuffersDataSpace(dataspace);
 }
 
+int Surface::dispatchSetSurfaceDamage(va_list args) {
+    android_native_rect_t* rects = va_arg(args, android_native_rect_t*);
+    size_t numRects = va_arg(args, size_t);
+    setSurfaceDamage(rects, numRects);
+    return NO_ERROR;
+}
+
 int Surface::connect(int api) {
     static sp<IProducerListener> listener = new DummyProducerListener();
     return connect(api, listener);
@@ -582,7 +617,13 @@
     }
     if (!err && api == NATIVE_WINDOW_API_CPU) {
         mConnectedToCpu = true;
+        // Clear the dirty region in case we're switching from a non-CPU API
+        mDirtyRegion.clear();
+    } else if (!err) {
+        // Initialize the dirty region for tracking surface damage
+        mDirtyRegion = Region::INVALID_REGION;
     }
+
     return err;
 }
 
@@ -800,6 +841,27 @@
     }
 }
 
+void Surface::setSurfaceDamage(android_native_rect_t* rects, size_t numRects) {
+    ATRACE_CALL();
+    ALOGV("Surface::setSurfaceDamage");
+    Mutex::Autolock lock(mMutex);
+
+    if (mConnectedToCpu || numRects == 0) {
+        mDirtyRegion = Region::INVALID_REGION;
+        return;
+    }
+
+    mDirtyRegion.clear();
+    for (size_t r = 0; r < numRects; ++r) {
+        // We intentionally flip top and bottom here, since because they're
+        // specified with a bottom-left origin, top > bottom, which fails
+        // validation in the Region class. We will fix this up when we flip to a
+        // top-left origin in queueBuffer.
+        Rect rect(rects[r].left, rects[r].bottom, rects[r].right, rects[r].top);
+        mDirtyRegion.orSelf(rect);
+    }
+}
+
 // ----------------------------------------------------------------------
 // the lock/unlock APIs must be used from the same thread
 
diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk
index 128a32a..6ad9986 100644
--- a/libs/gui/tests/Android.mk
+++ b/libs/gui/tests/Android.mk
@@ -3,6 +3,8 @@
 include $(CLEAR_VARS)
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
+LOCAL_CLANG := true
+
 LOCAL_MODULE := libgui_test
 
 LOCAL_MODULE_TAGS := tests
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index c38c797..1584fef 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -73,6 +73,8 @@
     virtual void onSidebandStreamChanged() {}
 };
 
+static const uint32_t TEST_DATA = 0x12345678u;
+
 // XXX: Tests that fork a process to hold the BufferQueue must run before tests
 // that use a local BufferQueue, or else Binder will get unhappy
 TEST_F(BufferQueueTest, BufferQueueInAnotherProcess) {
@@ -122,7 +124,7 @@
     uint32_t* dataIn;
     ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
             reinterpret_cast<void**>(&dataIn)));
-    *dataIn = 0x12345678;
+    *dataIn = TEST_DATA;
     ASSERT_EQ(OK, buffer->unlock());
 
     IGraphicBufferProducer::QueueBufferInput input(0, false,
@@ -136,7 +138,7 @@
     uint32_t* dataOut;
     ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
             reinterpret_cast<void**>(&dataOut)));
-    ASSERT_EQ(*dataOut, 0x12345678);
+    ASSERT_EQ(*dataOut, TEST_DATA);
     ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
 }
 
@@ -239,7 +241,7 @@
     uint32_t* dataIn;
     ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
             reinterpret_cast<void**>(&dataIn)));
-    *dataIn = 0x12345678;
+    *dataIn = TEST_DATA;
     ASSERT_EQ(OK, buffer->unlock());
 
     int newSlot;
@@ -258,7 +260,7 @@
     uint32_t* dataOut;
     ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
             reinterpret_cast<void**>(&dataOut)));
-    ASSERT_EQ(*dataOut, 0x12345678);
+    ASSERT_EQ(*dataOut, TEST_DATA);
     ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
 }
 
@@ -297,7 +299,7 @@
     ASSERT_EQ(OK, item.mGraphicBuffer->lock(
             GraphicBuffer::USAGE_SW_WRITE_OFTEN,
             reinterpret_cast<void**>(&dataIn)));
-    *dataIn = 0x12345678;
+    *dataIn = TEST_DATA;
     ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
 
     int newSlot;
@@ -317,7 +319,7 @@
     uint32_t* dataOut;
     ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
             reinterpret_cast<void**>(&dataOut)));
-    ASSERT_EQ(*dataOut, 0x12345678);
+    ASSERT_EQ(*dataOut, TEST_DATA);
     ASSERT_EQ(OK, buffer->unlock());
 }
 
@@ -340,7 +342,7 @@
     uint32_t* dataIn;
     ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
             reinterpret_cast<void**>(&dataIn)));
-    *dataIn = 0x12345678;
+    *dataIn = TEST_DATA;
     ASSERT_EQ(OK, buffer->unlock());
 
     IGraphicBufferProducer::QueueBufferInput input(0, false,
@@ -360,8 +362,44 @@
     uint32_t* dataOut;
     ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
             reinterpret_cast<void**>(&dataOut)));
-    ASSERT_EQ(*dataOut, 0x12345678);
+    ASSERT_EQ(*dataOut, TEST_DATA);
     ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
 }
 
+TEST_F(BufferQueueTest, TestDisallowingAllocation) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+            NATIVE_WINDOW_API_CPU, true, &output));
+
+    static const uint32_t WIDTH = 320;
+    static const uint32_t HEIGHT = 240;
+
+    ASSERT_EQ(OK, mConsumer->setDefaultBufferSize(WIDTH, HEIGHT));
+
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+    // This should return an error since it would require an allocation
+    ASSERT_EQ(OK, mProducer->allowAllocation(false));
+    ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, false, 0, 0,
+            0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+    // This should succeed, now that we've lifted the prohibition
+    ASSERT_EQ(OK, mProducer->allowAllocation(true));
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            mProducer->dequeueBuffer(&slot, &fence, false, 0, 0, 0,
+            GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+    // Release the previous buffer back to the BufferQueue
+    mProducer->cancelBuffer(slot, fence);
+
+    // This should fail since we're requesting a different size
+    ASSERT_EQ(OK, mProducer->allowAllocation(false));
+    ASSERT_EQ(WOULD_BLOCK, mProducer->dequeueBuffer(&slot, &fence, false,
+            WIDTH * 2, HEIGHT * 2, 0, GRALLOC_USAGE_SW_WRITE_OFTEN));
+}
+
 } // namespace android
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index 0beca92..2dc9ccc 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -94,7 +94,7 @@
             mPendingFrames--;
         }
 
-        virtual void onFrameAvailable() {
+        virtual void onFrameAvailable(const BufferItem&) {
             Mutex::Autolock lock(mMutex);
             mPendingFrames++;
             mCondition.signal();
@@ -125,7 +125,7 @@
             mPendingFrames--;
         }
 
-        virtual void onFrameAvailable() {
+        virtual void onFrameAvailable(const BufferItem&) {
             Mutex::Autolock lock(mMutex);
             mPendingFrames++;
             mFrameCondition.signal();
@@ -457,9 +457,12 @@
         const CpuConsumerTestParams& params,
         int maxBufferSlack) {
     status_t err;
-    err = native_window_set_buffers_geometry(anw.get(),
-            params.width, params.height, params.format);
-    ASSERT_NO_ERROR(err, "set_buffers_geometry error: ");
+    err = native_window_set_buffers_dimensions(anw.get(),
+            params.width, params.height);
+    ASSERT_NO_ERROR(err, "set_buffers_dimensions error: ");
+
+    err = native_window_set_buffers_format(anw.get(), params.format);
+    ASSERT_NO_ERROR(err, "set_buffers_format error: ");
 
     err = native_window_set_usage(anw.get(),
             GRALLOC_USAGE_SW_WRITE_OFTEN);
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index c904a6b..ff58420 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -38,31 +38,31 @@
 #define TEST_CONTROLLED_BY_APP false
 #define TEST_PRODUCER_USAGE_BITS (0)
 
-// TODO: Make these public constants in a header
-enum {
-    // Default dimensions before setDefaultBufferSize is called
-    DEFAULT_WIDTH = 1,
-    DEFAULT_HEIGHT = 1,
-
-    // Default format before setDefaultBufferFormat is called
-    DEFAULT_FORMAT = HAL_PIXEL_FORMAT_RGBA_8888,
-
-    // Default transform hint before setTransformHint is called
-    DEFAULT_TRANSFORM_HINT = 0,
-};
-
 namespace android {
 
 namespace {
-// Parameters for a generic "valid" input for queueBuffer.
-const int64_t QUEUE_BUFFER_INPUT_TIMESTAMP = 1384888611;
-const bool QUEUE_BUFFER_INPUT_IS_AUTO_TIMESTAMP = false;
-const android_dataspace QUEUE_BUFFER_INPUT_DATASPACE = HAL_DATASPACE_UNKNOWN;
-const Rect QUEUE_BUFFER_INPUT_RECT = Rect(DEFAULT_WIDTH, DEFAULT_HEIGHT);
-const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0;
-const int QUEUE_BUFFER_INPUT_TRANSFORM = 0;
-const bool QUEUE_BUFFER_INPUT_ASYNC = false;
-const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
+    // Default dimensions before setDefaultBufferSize is called
+    const uint32_t DEFAULT_WIDTH = 1;
+    const uint32_t DEFAULT_HEIGHT = 1;
+
+    // Default format before setDefaultBufferFormat is called
+    const PixelFormat DEFAULT_FORMAT = HAL_PIXEL_FORMAT_RGBA_8888;
+
+    // Default transform hint before setTransformHint is called
+    const uint32_t DEFAULT_TRANSFORM_HINT = 0;
+
+    // TODO: Make these constants in header
+    const int DEFAULT_CONSUMER_USAGE_BITS = 0;
+
+    // Parameters for a generic "valid" input for queueBuffer.
+    const int64_t QUEUE_BUFFER_INPUT_TIMESTAMP = 1384888611;
+    const bool QUEUE_BUFFER_INPUT_IS_AUTO_TIMESTAMP = false;
+    const android_dataspace QUEUE_BUFFER_INPUT_DATASPACE = HAL_DATASPACE_UNKNOWN;
+    const Rect QUEUE_BUFFER_INPUT_RECT = Rect(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+    const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0;
+    const int QUEUE_BUFFER_INPUT_TRANSFORM = 0;
+    const bool QUEUE_BUFFER_INPUT_ASYNC = false;
+    const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
 }; // namespace anonymous
 
 struct DummyConsumer : public BnConsumerListener {
@@ -273,15 +273,12 @@
 TEST_F(IGraphicBufferProducerTest, Query_Succeeds) {
     ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
-    // TODO: Make these constants in header
-    const int DEFAULT_CONSUMER_USAGE_BITS = 0;
-
-    int value = -1;
+    int32_t value = -1;
     EXPECT_OK(mProducer->query(NATIVE_WINDOW_WIDTH, &value));
-    EXPECT_EQ(DEFAULT_WIDTH, value);
+    EXPECT_EQ(DEFAULT_WIDTH, static_cast<uint32_t>(value));
 
     EXPECT_OK(mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
-    EXPECT_EQ(DEFAULT_HEIGHT, value);
+    EXPECT_EQ(DEFAULT_HEIGHT, static_cast<uint32_t>(value));
 
     EXPECT_OK(mProducer->query(NATIVE_WINDOW_FORMAT, &value));
     EXPECT_EQ(DEFAULT_FORMAT, value);
@@ -302,7 +299,7 @@
     ASSERT_NO_FATAL_FAILURE(ConnectProducer());
 
     // One past the end of the last 'query' enum value. Update this if we add more enums.
-    const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_CONSUMER_USAGE_BITS + 1;
+    const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_DEFAULT_DATASPACE + 1;
 
     int value;
     // What was out of range
@@ -369,7 +366,7 @@
         EXPECT_EQ(DEFAULT_WIDTH, width);
         EXPECT_EQ(DEFAULT_HEIGHT, height);
         EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint);
-        EXPECT_EQ(1, numPendingBuffers); // since queueBuffer was called exactly once
+        EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once
     }
 
     // Buffer was not in the dequeued state
diff --git a/libs/gui/tests/SRGB_test.cpp b/libs/gui/tests/SRGB_test.cpp
index e5907e7..3b11b97 100644
--- a/libs/gui/tests/SRGB_test.cpp
+++ b/libs/gui/tests/SRGB_test.cpp
@@ -17,6 +17,10 @@
 #define LOG_TAG "SRGB_test"
 //#define LOG_NDEBUG 0
 
+// Ignore for this file because it flags every instance of
+// ASSERT_EQ(GL_NO_ERROR, glGetError());
+#pragma clang diagnostic ignored "-Wsign-compare"
+
 #include "GLTest.h"
 
 #include <math.h>
@@ -329,9 +333,9 @@
         ANativeWindow_Buffer outBuffer;
         ARect outBufferBounds;
         mOutputSurface->lock(&outBuffer, &outBufferBounds);
-        ASSERT_EQ(mLockedBuffer.width, outBuffer.width);
-        ASSERT_EQ(mLockedBuffer.height, outBuffer.height);
-        ASSERT_EQ(mLockedBuffer.stride, outBuffer.stride);
+        ASSERT_EQ(mLockedBuffer.width, static_cast<uint32_t>(outBuffer.width));
+        ASSERT_EQ(mLockedBuffer.height, static_cast<uint32_t>(outBuffer.height));
+        ASSERT_EQ(mLockedBuffer.stride, static_cast<uint32_t>(outBuffer.stride));
 
         if (mLockedBuffer.format == outBuffer.format) {
             memcpy(outBuffer.bits, mLockedBuffer.data, bufferSize);
@@ -401,7 +405,8 @@
     // the debug surface if necessary
 }
 
-TEST_F(SRGBTest, RenderToSRGBSurface) {
+// XXX: Disabled since we don't currently expect this to work
+TEST_F(SRGBTest, DISABLED_RenderToSRGBSurface) {
     ASSERT_NO_FATAL_FAILURE(initShaders());
 
     // By default, the first buffer we write into will be RGB
diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp
index 767c7c6..00cc39d 100644
--- a/libs/gui/tests/StreamSplitter_test.cpp
+++ b/libs/gui/tests/StreamSplitter_test.cpp
@@ -76,6 +76,8 @@
     int mAllocCount;
 };
 
+static const uint32_t TEST_DATA = 0x12345678u;
+
 TEST_F(StreamSplitterTest, OneInputOneOutput) {
     sp<CountedAllocator> allocator(new CountedAllocator);
 
@@ -108,7 +110,7 @@
     uint32_t* dataIn;
     ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
             reinterpret_cast<void**>(&dataIn)));
-    *dataIn = 0x12345678;
+    *dataIn = TEST_DATA;
     ASSERT_EQ(OK, buffer->unlock());
 
     IGraphicBufferProducer::QueueBufferInput qbInput(0, false,
@@ -123,7 +125,7 @@
     uint32_t* dataOut;
     ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
             reinterpret_cast<void**>(&dataOut)));
-    ASSERT_EQ(*dataOut, 0x12345678);
+    ASSERT_EQ(*dataOut, TEST_DATA);
     ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
 
     ASSERT_EQ(OK, outputConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
@@ -175,7 +177,7 @@
     uint32_t* dataIn;
     ASSERT_EQ(OK, buffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
             reinterpret_cast<void**>(&dataIn)));
-    *dataIn = 0x12345678;
+    *dataIn = TEST_DATA;
     ASSERT_EQ(OK, buffer->unlock());
 
     IGraphicBufferProducer::QueueBufferInput qbInput(0, false,
@@ -191,7 +193,7 @@
         uint32_t* dataOut;
         ASSERT_EQ(OK, item.mGraphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_OFTEN,
                     reinterpret_cast<void**>(&dataOut)));
-        ASSERT_EQ(*dataOut, 0x12345678);
+        ASSERT_EQ(*dataOut, TEST_DATA);
         ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
 
         ASSERT_EQ(OK, outputConsumers[output]->releaseBuffer(item.mBuf,
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 8cdf3bc..d750cd0 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -207,12 +207,8 @@
 }
 
 TEST_F(SurfaceTextureClientTest, BufferGeometryInvalidSizesFail) {
-    EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(), -1,  0,  0));
-    EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(),  0, -1,  0));
-    EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(),  0,  0, -1));
-    EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(), -1, -1,  0));
-    EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(),  0,  8,  0));
-    EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(),  8,  0,  0));
+    EXPECT_GT(OK, native_window_set_buffers_dimensions(mANW.get(),  0,  8));
+    EXPECT_GT(OK, native_window_set_buffers_dimensions(mANW.get(),  8,  0));
 }
 
 TEST_F(SurfaceTextureClientTest, DefaultGeometryValues) {
@@ -226,7 +222,8 @@
 
 TEST_F(SurfaceTextureClientTest, BufferGeometryCanBeSet) {
     ANativeWindowBuffer* buf;
-    EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 16, 8, PIXEL_FORMAT_RGB_565));
+    EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 16, 8));
+    EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), PIXEL_FORMAT_RGB_565));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf));
     EXPECT_EQ(16, buf->width);
     EXPECT_EQ(8, buf->height);
@@ -236,7 +233,8 @@
 
 TEST_F(SurfaceTextureClientTest, BufferGeometryDefaultSizeSetFormat) {
     ANativeWindowBuffer* buf;
-    EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 0, 0, PIXEL_FORMAT_RGB_565));
+    EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 0, 0));
+    EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), PIXEL_FORMAT_RGB_565));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf));
     EXPECT_EQ(1, buf->width);
     EXPECT_EQ(1, buf->height);
@@ -246,7 +244,8 @@
 
 TEST_F(SurfaceTextureClientTest, BufferGeometrySetSizeDefaultFormat) {
     ANativeWindowBuffer* buf;
-    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));
+    EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf));
     EXPECT_EQ(16, buf->width);
     EXPECT_EQ(8, buf->height);
@@ -256,13 +255,15 @@
 
 TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeUnset) {
     ANativeWindowBuffer* buf;
-    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));
+    EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf));
     EXPECT_EQ(16, buf->width);
     EXPECT_EQ(8, buf->height);
     EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
     ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf, -1));
-    EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 0, 0, 0));
+    EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 0, 0));
+    EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf));
     EXPECT_EQ(1, buf->width);
     EXPECT_EQ(1, buf->height);
@@ -272,7 +273,8 @@
 
 TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) {
     ANativeWindowBuffer* buf;
-    EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 0, 0, PIXEL_FORMAT_RGB_565));
+    EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 0, 0));
+    EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), PIXEL_FORMAT_RGB_565));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf));
     EXPECT_EQ(1, buf->width);
     EXPECT_EQ(1, buf->height);
@@ -330,7 +332,8 @@
     EXPECT_EQ(8, buf[1]->height);
     ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1));
     ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1], -1));
-    EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 12, 24, 0));
+    EXPECT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 12, 24));
+    EXPECT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1]));
     EXPECT_NE(buf[0], buf[1]);
@@ -468,7 +471,8 @@
 
     // Once we've queued a buffer, however we should not be able to dequeue more
     // than (buffer-count - MIN_UNDEQUEUED_BUFFERS), which is 2 in this case.
-    EXPECT_EQ(-EBUSY, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1]));
+    EXPECT_EQ(INVALID_OPERATION,
+            native_window_dequeue_buffer_and_wait(mANW.get(), &buf[1]));
 
     ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0], -1));
     ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2], -1));
@@ -620,7 +624,8 @@
     crop.bottom = 5;
 
     ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4));
-    ASSERT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 8, 8, 0));
+    ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 8, 8));
+    ASSERT_EQ(OK, native_window_set_buffers_format(mANW.get(), 0));
     ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf[0]));
     ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &crop));
     ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0], -1));
@@ -668,7 +673,8 @@
     const int numFmts = (sizeof(fmts) / sizeof(fmts[0]));
     for (int i = 0; i < numFmts; i++) {
       int fmt = -1;
-      ASSERT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, fmts[i]));
+      ASSERT_EQ(OK, native_window_set_buffers_dimensions(anw.get(), 0, 0));
+      ASSERT_EQ(OK, native_window_set_buffers_format(anw.get(), fmts[i]));
       ASSERT_EQ(OK, anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &fmt));
       EXPECT_EQ(fmts[i], fmt);
     }
diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp
index b165ae6..c243fc0 100644
--- a/libs/gui/tests/SurfaceTextureFBO_test.cpp
+++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp
@@ -27,8 +27,10 @@
     const int texWidth = 64;
     const int texHeight = 64;
 
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
+            texWidth, texHeight));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_RGBA_8888));
     ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
             GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
 
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index fa1e1b7..fad133f 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -28,8 +28,10 @@
     const int texWidth = 64;
     const int texHeight = 66;
 
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            texWidth, texHeight, HAL_PIXEL_FORMAT_YV12));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
+            texWidth, texHeight));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_YV12));
     ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
             GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
 
@@ -74,8 +76,10 @@
     const int texWidth = 64;
     const int texHeight = 64;
 
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            texWidth, texHeight, HAL_PIXEL_FORMAT_YV12));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
+            texWidth, texHeight));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_YV12));
     ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
             GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
 
@@ -120,8 +124,10 @@
     const int texWidth = 64;
     const int texHeight = 66;
 
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            texWidth, texHeight, HAL_PIXEL_FORMAT_YV12));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
+            texWidth, texHeight));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_YV12));
     ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
             GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
 
@@ -185,8 +191,10 @@
     enum { numFrames = 1024 };
 
     ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2));
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            texWidth, texHeight, HAL_PIXEL_FORMAT_YV12));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
+            texWidth, texHeight));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_YV12));
     ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
             GRALLOC_USAGE_SW_WRITE_OFTEN));
 
@@ -326,8 +334,10 @@
     const int texWidth = 64;
     const int texHeight = 66;
 
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
+            texWidth, texHeight));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_RGBA_8888));
     ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
             GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
 
@@ -368,8 +378,10 @@
     const int texWidth = 64;
     const int texHeight = 64;
 
-    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
-            texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
+            texWidth, texHeight));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+            HAL_PIXEL_FORMAT_RGBA_8888));
     ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
             GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
 
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 9cf2881..bf24ffb 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -130,7 +130,7 @@
 }
 
 size_t Fence::getFlattenedSize() const {
-    return 1;
+    return 4;
 }
 
 size_t Fence::getFdCount() const {
@@ -141,7 +141,9 @@
     if (size < getFlattenedSize() || count < getFdCount()) {
         return NO_MEMORY;
     }
-    FlattenableUtils::write(buffer, size, getFdCount());
+    // Cast to uint32_t since the size of a size_t can vary between 32- and
+    // 64-bit processes
+    FlattenableUtils::write(buffer, size, static_cast<uint32_t>(getFdCount()));
     if (isValid()) {
         *fds++ = mFenceFd;
         count--;
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 638ac62..6a42a22 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -152,6 +152,16 @@
     return initSize(inWidth, inHeight, inFormat, inUsage);
 }
 
+bool GraphicBuffer::needsReallocation(uint32_t inWidth, uint32_t inHeight,
+        PixelFormat inFormat, uint32_t inUsage)
+{
+    if (static_cast<int>(inWidth) != width) return true;
+    if (static_cast<int>(inHeight) != height) return true;
+    if (inFormat != format) return true;
+    if ((static_cast<uint32_t>(usage) & inUsage) != inUsage) return true;
+    return false;
+}
+
 status_t GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight,
         PixelFormat inFormat, uint32_t inUsage)
 {
@@ -303,7 +313,7 @@
                 static_cast<size_t>(handle->numInts) * sizeof(int));
     }
 
-    buffer = reinterpret_cast<void*>(static_cast<int*>(buffer) + sizeNeeded);
+    buffer = static_cast<void*>(static_cast<uint8_t*>(buffer) + sizeNeeded);
     size -= sizeNeeded;
     if (handle) {
         fds += handle->numFds;
@@ -385,7 +395,7 @@
         }
     }
 
-    buffer = reinterpret_cast<void const*>(static_cast<int const*>(buffer) + sizeNeeded);
+    buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
     size -= sizeNeeded;
     fds += numFds;
     count -= numFds;
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index b480f3a..dcce21f 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -19,6 +19,8 @@
 
 namespace android {
 
+const Rect Rect::INVALID_RECT{0, 0, -1, -1};
+
 static inline int32_t min(int32_t a, int32_t b) {
     return (a < b) ? a : b;
 }
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 91fa216..3810da4 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -53,6 +53,8 @@
     direction_RTL
 };
 
+const Region Region::INVALID_REGION(Rect::INVALID_RECT);
+
 // ----------------------------------------------------------------------------
 
 Region::Region() {
@@ -130,43 +132,42 @@
             // prevIndex can't be -1 here because if endLastSpan is set to a
             // value greater than -1 (allowing the loop to execute),
             // beginLastSpan (and therefore prevIndex) will also be increased
-            const Rect* prev = &dst[static_cast<size_t>(prevIndex)];
-
+            const Rect prev = dst[static_cast<size_t>(prevIndex)];
             if (spanDirection == direction_RTL) {
                 // iterating over previous span RTL, quit if it's too far left
-                if (prev->right <= left) break;
+                if (prev.right <= left) break;
 
-                if (prev->right > left && prev->right < right) {
-                    dst.add(Rect(prev->right, top, right, bottom));
-                    right = prev->right;
+                if (prev.right > left && prev.right < right) {
+                    dst.add(Rect(prev.right, top, right, bottom));
+                    right = prev.right;
                 }
 
-                if (prev->left > left && prev->left < right) {
-                    dst.add(Rect(prev->left, top, right, bottom));
-                    right = prev->left;
+                if (prev.left > left && prev.left < right) {
+                    dst.add(Rect(prev.left, top, right, bottom));
+                    right = prev.left;
                 }
 
                 // if an entry in the previous span is too far right, nothing further left in the
                 // current span will need it
-                if (prev->left >= right) {
+                if (prev.left >= right) {
                     beginLastSpan = prevIndex;
                 }
             } else {
                 // iterating over previous span LTR, quit if it's too far right
-                if (prev->left >= right) break;
+                if (prev.left >= right) break;
 
-                if (prev->left > left && prev->left < right) {
-                    dst.add(Rect(left, top, prev->left, bottom));
-                    left = prev->left;
+                if (prev.left > left && prev.left < right) {
+                    dst.add(Rect(left, top, prev.left, bottom));
+                    left = prev.left;
                 }
 
-                if (prev->right > left && prev->right < right) {
-                    dst.add(Rect(left, top, prev->right, bottom));
-                    left = prev->right;
+                if (prev.right > left && prev.right < right) {
+                    dst.add(Rect(left, top, prev.right, bottom));
+                    left = prev.right;
                 }
                 // if an entry in the previous span is too far left, nothing further right in the
                 // current span will need it
-                if (prev->right <= left) {
+                if (prev.right <= left) {
                     beginLastSpan = prevIndex;
                 }
             }
@@ -518,8 +519,12 @@
     Rect b(*prev);
     while (cur != tail) {
         if (cur->isValid() == false) {
-            ALOGE_IF(!silent, "%s: region contains an invalid Rect", name);
-            result = false;
+            // We allow this particular flavor of invalid Rect, since it is used
+            // as a signal value in various parts of the system
+            if (*cur != Rect::INVALID_RECT) {
+                ALOGE_IF(!silent, "%s: region contains an invalid Rect", name);
+                result = false;
+            }
         }
         if (cur->right > region_operator<Rect>::max_value) {
             ALOGE_IF(!silent, "%s: rect->right > max_value", name);
@@ -691,7 +696,9 @@
         const Region& lhs,
         const Rect& rhs, int dx, int dy)
 {
-    if (!rhs.isValid()) {
+    // We allow this particular flavor of invalid Rect, since it is used as a
+    // signal value in various parts of the system
+    if (!rhs.isValid() && rhs != Rect::INVALID_RECT) {
         ALOGE("Region::boolean_operation(op=%d) invalid Rect={%d,%d,%d,%d}",
                 op, rhs.left, rhs.top, rhs.right, rhs.bottom);
         return;
@@ -754,35 +761,52 @@
 // ----------------------------------------------------------------------------
 
 size_t Region::getFlattenedSize() const {
-    return mStorage.size() * sizeof(Rect);
+    return sizeof(uint32_t) + mStorage.size() * sizeof(Rect);
 }
 
 status_t Region::flatten(void* buffer, size_t size) const {
 #if VALIDATE_REGIONS
     validate(*this, "Region::flatten");
 #endif
-    if (size < mStorage.size() * sizeof(Rect)) {
+    if (size < getFlattenedSize()) {
         return NO_MEMORY;
     }
-    Rect* rects = reinterpret_cast<Rect*>(buffer);
-    memcpy(rects, mStorage.array(), mStorage.size() * sizeof(Rect));
+    // Cast to uint32_t since the size of a size_t can vary between 32- and
+    // 64-bit processes
+    FlattenableUtils::write(buffer, size, static_cast<uint32_t>(mStorage.size()));
+    for (auto rect : mStorage) {
+        status_t result = rect.flatten(buffer, size);
+        if (result != NO_ERROR) {
+            return result;
+        }
+        FlattenableUtils::advance(buffer, size, sizeof(rect));
+    }
     return NO_ERROR;
 }
 
 status_t Region::unflatten(void const* buffer, size_t size) {
-    Region result;
-    if (size >= sizeof(Rect)) {
-        Rect const* rects = reinterpret_cast<Rect const*>(buffer);
-        size_t count = size / sizeof(Rect);
-        if (count > 0) {
-            result.mStorage.clear();
-            ssize_t err = result.mStorage.insertAt(0, count);
-            if (err < 0) {
-                return status_t(err);
-            }
-            memcpy(result.mStorage.editArray(), rects, count*sizeof(Rect));
-        }
+    if (size < sizeof(uint32_t)) {
+        return NO_MEMORY;
     }
+
+    uint32_t numRects = 0;
+    FlattenableUtils::read(buffer, size, numRects);
+    if (size < numRects * sizeof(Rect)) {
+        return NO_MEMORY;
+    }
+
+    Region result;
+    result.mStorage.clear();
+    for (size_t r = 0; r < numRects; ++r) {
+        Rect rect;
+        status_t status = rect.unflatten(buffer, size);
+        if (status != NO_ERROR) {
+            return status;
+        }
+        FlattenableUtils::advance(buffer, size, sizeof(rect));
+        result.mStorage.push_back(rect);
+    }
+
 #if VALIDATE_REGIONS
     validate(result, "Region::unflatten");
 #endif
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 3b2984a..25f7607 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -176,6 +176,15 @@
 #define EGL_BITMAP_PIXEL_SIZE_KHR		0x3110
 #endif
 
+#ifndef EGL_KHR_partial_update
+#define EGL_KHR_partial_update 1
+#define EGL_BUFFER_AGE_KHR                0x313D
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETDAMAGEREGIONKHRPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglSetDamageRegionKHR (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#endif
+#endif /* EGL_KHR_partial_update */
+
 #ifndef EGL_NV_coverage_sample
 #define EGL_NV_coverage_sample 1
 #define EGL_COVERAGE_BUFFERS_NV			0x30E0
@@ -440,6 +449,14 @@
 /* No tokens/entry points, just relaxes an error condition */
 #endif
 
+#ifndef EGL_KHR_swap_buffers_with_damage
+#define EGL_KHR_swap_buffers_with_damage 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+#endif
+#endif /* EGL_KHR_swap_buffers_with_damage */
+
 #ifdef EGL_KHR_stream /* Requires KHR_stream extension */
 #ifndef EGL_KHR_stream_cross_process_fd
 #define EGL_KHR_stream_cross_process_fd 1
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 11a13c3..f5b90dd 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -80,6 +80,7 @@
 extern char const * const gBuiltinExtensionString =
         "EGL_KHR_get_all_proc_addresses "
         "EGL_ANDROID_presentation_time "
+        "EGL_KHR_swap_buffers_with_damage "
         ;
 extern char const * const gExtensionString  =
         "EGL_KHR_image "                        // mandatory
@@ -100,6 +101,8 @@
         "EGL_ANDROID_image_native_buffer "      // mandatory
         "EGL_KHR_wait_sync "                    // strongly recommended
         "EGL_ANDROID_recordable "               // mandatory
+        "EGL_KHR_partial_update "               // strongly recommended
+        "EGL_EXT_buffer_age "                   // strongly recommended with partial_update
         ;
 
 // extensions not exposed to applications but used by the ANDROID system
@@ -152,6 +155,14 @@
     // EGL_ANDROID_presentation_time
     { "eglPresentationTimeANDROID",
             (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID },
+
+    // EGL_KHR_swap_buffers_with_damage
+    { "eglSwapBuffersWithDamageKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR },
+
+    // EGL_KHR_partial_update
+    { "eglSetDamageRegionKHR",
+            (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR },
 };
 
 /*
@@ -1021,7 +1032,8 @@
     Mutex mMutex;
 };
 
-EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
+EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw,
+        EGLint *rects, EGLint n_rects)
 {
     ATRACE_CALL();
     clearError();
@@ -1080,7 +1092,38 @@
         }
     }
 
-    return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+    if (n_rects == 0) {
+        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+    }
+
+    Vector<android_native_rect_t> androidRects;
+    for (int r = 0; r < n_rects; ++r) {
+        int offset = r * 4;
+        int x = rects[offset];
+        int y = rects[offset + 1];
+        int width = rects[offset + 2];
+        int height = rects[offset + 3];
+        android_native_rect_t androidRect;
+        androidRect.left = x;
+        androidRect.top = y + height;
+        androidRect.right = x + width;
+        androidRect.bottom = y;
+        androidRects.push_back(androidRect);
+    }
+    native_window_set_surface_damage(s->win.get(), androidRects.array(),
+            androidRects.size());
+
+    if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
+        return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
+                rects, n_rects);
+    } else {
+        return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+    }
+}
+
+EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
+{
+    return eglSwapBuffersWithDamageKHR(dpy, surface, NULL, 0);
 }
 
 EGLBoolean eglCopyBuffers(  EGLDisplay dpy, EGLSurface surface,
@@ -1569,3 +1612,32 @@
 
     return setErrorQuiet(EGL_BAD_DISPLAY, 0);
 }
+
+// ----------------------------------------------------------------------------
+// Partial update extension
+// ----------------------------------------------------------------------------
+EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface,
+        EGLint *rects, EGLint n_rects)
+{
+    clearError();
+
+    const egl_display_ptr dp = validate_display(dpy);
+    if (!dp) {
+        setError(EGL_BAD_DISPLAY, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    SurfaceRef _s(dp.get(), surface);
+    if (!_s.get()) {
+        setError(EGL_BAD_SURFACE, EGL_FALSE);
+        return EGL_FALSE;
+    }
+
+    egl_surface_t const * const s = get_surface(surface);
+    if (s->cnx->egl.eglSetDamageRegionKHR) {
+        return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface,
+                rects, n_rects);
+    }
+
+    return EGL_FALSE;
+}
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 70d0e52..1e27cb6 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -89,4 +89,9 @@
 /* IMG extensions */
 
 EGL_ENTRY(EGLBoolean, eglHibernateProcessIMG, void)
-EGL_ENTRY(EGLBoolean, eglAwakenProcessIMG, void)
\ No newline at end of file
+EGL_ENTRY(EGLBoolean, eglAwakenProcessIMG, void)
+
+/* Partial update extensions */
+
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
+EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 77a9a19..c8b36ec 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -1020,6 +1020,21 @@
         SharedBuffer const* sb = reg.getSharedBuffer(&visibleRegion.numRects);
         visibleRegion.rects = reinterpret_cast<hwc_rect_t const *>(sb->data());
     }
+    virtual void setSurfaceDamage(const Region& reg) {
+        if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_5)) {
+            return;
+        }
+        hwc_region_t& surfaceDamage = getLayer()->surfaceDamage;
+        // We encode default full-screen damage as INVALID_RECT upstream, but as
+        // 0 rects for HWComposer
+        if (reg.isRect() && reg.getBounds() == Rect::INVALID_RECT) {
+            surfaceDamage.numRects = 0;
+            surfaceDamage.rects = NULL;
+            return;
+        }
+        SharedBuffer const* sb = reg.getSharedBuffer(&surfaceDamage.numRects);
+        surfaceDamage.rects = reinterpret_cast<hwc_rect_t const *>(sb->data());
+    }
     virtual void setSidebandStream(const sp<NativeHandle>& stream) {
         ALOG_ASSERT(stream->handle() != NULL);
         getLayer()->compositionType = HWC_SIDEBAND;
@@ -1050,6 +1065,18 @@
         }
 
         getLayer()->acquireFenceFd = -1;
+
+        if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_5)) {
+            return;
+        }
+
+        hwc_region_t& surfaceDamage = getLayer()->surfaceDamage;
+        sb = SharedBuffer::bufferFromData(surfaceDamage.rects);
+        if (sb) {
+            sb->release();
+            surfaceDamage.numRects = 0;
+            surfaceDamage.rects = NULL;
+        }
     }
 };
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index a62ac5c..28d8c65 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -168,6 +168,7 @@
         virtual void setFrame(const Rect& frame) = 0;
         virtual void setCrop(const FloatRect& crop) = 0;
         virtual void setVisibleRegionScreen(const Region& reg) = 0;
+        virtual void setSurfaceDamage(const Region& reg) = 0;
         virtual void setSidebandStream(const sp<NativeHandle>& stream) = 0;
         virtual void setBuffer(const sp<GraphicBuffer>& buffer) = 0;
         virtual void setAcquireFenceFd(int fenceFd) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 0e94f0d..11cbdc6 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -526,6 +526,10 @@
     // TODO: Should we actually allocate buffers for a virtual display?
 }
 
+status_t VirtualDisplaySurface::allowAllocation(bool /* allow */) {
+    return INVALID_OPERATION;
+}
+
 void VirtualDisplaySurface::updateQueueBufferOutput(
         const QueueBufferOutput& qbo) {
     uint32_t w, h, transformHint, numPendingBuffers;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 0a3f4a1..97af980 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -115,6 +115,7 @@
     virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
     virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
             PixelFormat format, uint32_t usage);
+    virtual status_t allowAllocation(bool allow);
 
     //
     // Utility methods
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index 9b6360e..f760200 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -71,6 +71,11 @@
     mVsyncHintSent = false;
 }
 
+void EventThread::setPhaseOffset(nsecs_t phaseOffset) {
+    Mutex::Autolock _l(mLock);
+    mVSyncSource->setPhaseOffset(phaseOffset);
+}
+
 void EventThread::sendVsyncHintOnLocked() {
     struct itimerspec ts;
     if(!mVsyncHintSent) {
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index d1c4fcd..9ba179a 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -51,6 +51,7 @@
     virtual ~VSyncSource() {}
     virtual void setVSyncEnabled(bool enable) = 0;
     virtual void setCallback(const sp<Callback>& callback) = 0;
+    virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
 };
 
 class EventThread : public Thread, private VSyncSource::Callback {
@@ -99,6 +100,8 @@
     void dump(String8& result) const;
     void sendVsyncHintOff();
 
+    void setPhaseOffset(nsecs_t phaseOffset);
+
 private:
     virtual bool        threadLoop();
     virtual void        onFirstRef();
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2ac4765..2944c63 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -513,6 +513,16 @@
     Region visible = tr.transform(visibleRegion.intersect(hw->getViewport()));
     layer.setVisibleRegionScreen(visible);
 
+    // Pass full-surface damage down untouched
+    if (surfaceDamageRegion.isRect() &&
+            surfaceDamageRegion.getBounds() == Rect::INVALID_RECT) {
+        layer.setSurfaceDamage(surfaceDamageRegion);
+    } else {
+        Region surfaceDamage =
+            tr.transform(surfaceDamageRegion.intersect(hw->getViewport()));
+        layer.setSurfaceDamage(surfaceDamage);
+    }
+
     if (mSidebandStream.get()) {
         layer.setSidebandStream(mSidebandStream);
     } else {
@@ -908,7 +918,7 @@
         const bool resizePending = (c.requested.w != c.active.w) ||
                                    (c.requested.h != c.active.h);
 
-        if (resizePending) {
+        if (resizePending && mSidebandStream == NULL) {
             // don't let Layer::doTransaction update the drawing state
             // if we have a pending resize, unless we are in fixed-size mode.
             // the drawing state will be updated only once we receive a buffer
@@ -917,6 +927,10 @@
             // in particular, we want to make sure the clip (which is part
             // of the geometry state) is latched together with the size but is
             // latched immediately when no resizing is involved.
+            //
+            // If a sideband stream is attached, however, we want to skip this
+            // optimization so that transactions aren't missed when a buffer
+            // never arrives
 
             flags |= eDontUpdateGeometryState;
         }
@@ -1034,6 +1048,18 @@
     return true;
 }
 
+void Layer::useSurfaceDamage() {
+    if (mFlinger->mForceFullDamage) {
+        surfaceDamageRegion = Region::INVALID_REGION;
+    } else {
+        surfaceDamageRegion = mSurfaceFlingerConsumer->getSurfaceDamage();
+    }
+}
+
+void Layer::useEmptyDamage() {
+    surfaceDamageRegion.clear();
+}
+
 // ----------------------------------------------------------------------------
 // pageflip handling...
 // ----------------------------------------------------------------------------
@@ -1349,6 +1375,7 @@
 
     s.activeTransparentRegion.dump(result, "transparentRegion");
     visibleRegion.dump(result, "visibleRegion");
+    surfaceDamageRegion.dump(result, "surfaceDamageRegion");
     sp<Client> client(mClientRef.promote());
 
     result.appendFormat(            "      "
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 424c03e..46c17e5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -76,6 +76,7 @@
     Region visibleRegion;
     Region coveredRegion;
     Region visibleNonTransparentRegion;
+    Region surfaceDamageRegion;
 
     // Layer serial number.  This gives layers an explicit ordering, so we
     // have a stable sort order when their layer stack and Z-order are
@@ -137,6 +138,12 @@
     bool setCrop(const Rect& crop);
     bool setLayerStack(uint32_t layerStack);
 
+    // If we have received a new buffer this frame, we will pass its surface
+    // damage down to hardware composer. Otherwise, we must send a region with
+    // one empty rect.
+    void useSurfaceDamage();
+    void useEmptyDamage();
+
     uint32_t getTransactionFlags(uint32_t flags);
     uint32_t setTransactionFlags(uint32_t flags);
 
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index e4e7d42..9fb555b 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -110,6 +110,10 @@
     mProducer->allocateBuffers(async, width, height, format, usage);
 }
 
+status_t MonitoredProducer::allowAllocation(bool allow) {
+    return mProducer->allowAllocation(allow);
+}
+
 IBinder* MonitoredProducer::onAsBinder() {
     return IInterface::asBinder(mProducer).get();
 }
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index aec3e85..b2f8293 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -53,6 +53,7 @@
     virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
     virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
             PixelFormat format, uint32_t usage);
+    virtual status_t allowAllocation(bool allow);
     virtual IBinder* onAsBinder();
 
 private:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9dc140e..df4ac2e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -145,6 +145,7 @@
         mDebugInTransaction(0),
         mLastTransactionTime(0),
         mBootFinished(false),
+        mForceFullDamage(false),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
         mDaltonize(false),
@@ -319,17 +320,20 @@
     DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
         const char* label) :
             mValue(0),
-            mPhaseOffset(phaseOffset),
             mTraceVsync(traceVsync),
             mVsyncOnLabel(String8::format("VsyncOn-%s", label)),
             mVsyncEventLabel(String8::format("VSYNC-%s", label)),
-            mDispSync(dispSync) {}
+            mDispSync(dispSync),
+            mCallbackMutex(),
+            mCallback(),
+            mVsyncMutex(),
+            mPhaseOffset(phaseOffset),
+            mEnabled(false) {}
 
     virtual ~DispSyncSource() {}
 
     virtual void setVSyncEnabled(bool enable) {
-        // Do NOT lock the mutex here so as to avoid any mutex ordering issues
-        // with locking it in the onDispSyncEvent callback.
+        Mutex::Autolock lock(mVsyncMutex);
         if (enable) {
             status_t err = mDispSync->addEventListener(mPhaseOffset,
                     static_cast<DispSync::Callback*>(this));
@@ -347,18 +351,54 @@
             }
             //ATRACE_INT(mVsyncOnLabel.string(), 0);
         }
+        mEnabled = enable;
     }
 
     virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
-        Mutex::Autolock lock(mMutex);
+        Mutex::Autolock lock(mCallbackMutex);
         mCallback = callback;
     }
 
+    virtual void setPhaseOffset(nsecs_t phaseOffset) {
+        Mutex::Autolock lock(mVsyncMutex);
+
+        // Normalize phaseOffset to [0, period)
+        auto period = mDispSync->getPeriod();
+        phaseOffset %= period;
+        if (phaseOffset < 0) {
+            // If we're here, then phaseOffset is in (-period, 0). After this
+            // operation, it will be in (0, period)
+            phaseOffset += period;
+        }
+        mPhaseOffset = phaseOffset;
+
+        // If we're not enabled, we don't need to mess with the listeners
+        if (!mEnabled) {
+            return;
+        }
+
+        // Remove the listener with the old offset
+        status_t err = mDispSync->removeEventListener(
+                static_cast<DispSync::Callback*>(this));
+        if (err != NO_ERROR) {
+            ALOGE("error unregistering vsync callback: %s (%d)",
+                    strerror(-err), err);
+        }
+
+        // Add a listener with the new offset
+        err = mDispSync->addEventListener(mPhaseOffset,
+                static_cast<DispSync::Callback*>(this));
+        if (err != NO_ERROR) {
+            ALOGE("error registering vsync callback: %s (%d)",
+                    strerror(-err), err);
+        }
+    }
+
 private:
     virtual void onDispSyncEvent(nsecs_t when) {
         sp<VSyncSource::Callback> callback;
         {
-            Mutex::Autolock lock(mMutex);
+            Mutex::Autolock lock(mCallbackMutex);
             callback = mCallback;
 
             if (mTraceVsync) {
@@ -374,14 +414,18 @@
 
     int mValue;
 
-    const nsecs_t mPhaseOffset;
     const bool mTraceVsync;
     const String8 mVsyncOnLabel;
     const String8 mVsyncEventLabel;
 
     DispSync* mDispSync;
+
+    Mutex mCallbackMutex; // Protects the following
     sp<VSyncSource::Callback> mCallback;
-    Mutex mMutex;
+
+    Mutex mVsyncMutex; // Protects the following
+    nsecs_t mPhaseOffset;
+    bool mEnabled;
 };
 
 void SurfaceFlinger::init() {
@@ -1725,12 +1769,17 @@
             frameQueued = true;
             if (layer->shouldPresentNow(mPrimaryDispSync)) {
                 layersWithQueuedFrames.push_back(layer.get());
+            } else {
+                layer->useEmptyDamage();
             }
+        } else {
+            layer->useEmptyDamage();
         }
     }
     for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) {
         Layer* layer = layersWithQueuedFrames[i];
         const Region dirty(layer->latchBuffer(visibleRegions));
+        layer->useSurfaceDamage();
         const Layer::State& s(layer->getDrawingState());
         invalidateLayerStack(s.layerStack, dirty);
     }
@@ -2876,6 +2925,21 @@
                 mPrimaryDispSync.setRefreshSkipCount(n);
                 return NO_ERROR;
             }
+            case 1017: {
+                n = data.readInt32();
+                mForceFullDamage = static_cast<bool>(n);
+                return NO_ERROR;
+            }
+            case 1018: { // Modify Choreographer's phase offset
+                n = data.readInt32();
+                mEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+                return NO_ERROR;
+            }
+            case 1019: { // Modify SurfaceFlinger's phase offset
+                n = data.readInt32();
+                mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+                return NO_ERROR;
+            }
         }
     }
     return err;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 4deb815..a06d1be 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -469,6 +469,7 @@
     volatile nsecs_t mDebugInTransaction;
     nsecs_t mLastTransactionTime;
     bool mBootFinished;
+    bool mForceFullDamage;
 
     // these are thread safe
     mutable MessageQueue mEventQueue;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index abf3993..19c497a 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -108,6 +108,7 @@
     status_t result = GLConsumer::acquireBufferLocked(item, presentWhen);
     if (result == NO_ERROR) {
         mTransformToDisplayInverse = item->mTransformToDisplayInverse;
+        mSurfaceDamage = item->mSurfaceDamage;
     }
     return result;
 }
@@ -116,6 +117,10 @@
     return mTransformToDisplayInverse;
 }
 
+const Region& SurfaceFlingerConsumer::getSurfaceDamage() const {
+    return mSurfaceDamage;
+}
+
 sp<NativeHandle> SurfaceFlingerConsumer::getSidebandStream() const {
     return mConsumer->getSidebandStream();
 }
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index d1cf9b9..1aaba18 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -35,7 +35,7 @@
     SurfaceFlingerConsumer(const sp<IGraphicBufferConsumer>& consumer,
             uint32_t tex)
         : GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false, false),
-          mTransformToDisplayInverse(false)
+          mTransformToDisplayInverse(false), mSurfaceDamage()
     {}
 
     class BufferRejecter {
@@ -60,6 +60,7 @@
 
     // must be called from SF main thread
     bool getTransformToDisplayInverse() const;
+    const Region& getSurfaceDamage() const;
 
     // Sets the contents changed listener. This should be used instead of
     // ConsumerBase::setFrameAvailableListener().
@@ -78,6 +79,9 @@
     // it is displayed onto. This is applied after GLConsumer::mCurrentTransform.
     // This must be set/read from SurfaceFlinger's main thread.
     bool mTransformToDisplayInverse;
+
+    // The portion of this surface that has changed since the previous frame
+    Region mSurfaceDamage;
 };
 
 // ----------------------------------------------------------------------------