Merge "OMX IL header additions for FLAC" into jb-dev
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
index 81b56c2..8b84951 100644
--- a/include/binder/IBinder.h
+++ b/include/binder/IBinder.h
@@ -51,6 +51,7 @@
         PING_TRANSACTION        = B_PACK_CHARS('_','P','N','G'),
         DUMP_TRANSACTION        = B_PACK_CHARS('_','D','M','P'),
         INTERFACE_TRANSACTION   = B_PACK_CHARS('_', 'N', 'T', 'F'),
+        SYSPROPS_TRANSACTION    = B_PACK_CHARS('_', 'S', 'P', 'R'),
 
         // Corresponds to TF_ONE_WAY -- an asynchronous call.
         FLAG_ONEWAY             = 0x00000001
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index e7fac3d..1c80d0c 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -170,7 +170,6 @@
            mFrameNumber(0),
            mBuf(INVALID_BUFFER_SLOT) {
              mCrop.makeInvalid();
-             mActiveRect.makeInvalid();
          }
         // mGraphicBuffer points to the buffer allocated for this slot or is NULL
         // if no buffer has been allocated.
@@ -194,11 +193,6 @@
 
         // mBuf is the slot index of this buffer
         int mBuf;
-
-        // mActiveRect is the active rectangle for the buffer.  Pixels outside
-        // this rectangle are considered completely transparent for the purposes
-        // of window composition.
-        Rect mActiveRect;
     };
 
     // The following public functions is the consumer facing interface
@@ -302,7 +296,6 @@
           mAcquireCalled(false),
           mNeedsCleanupOnRelease(false) {
             mCrop.makeInvalid();
-            mActiveRect.makeInvalid();
         }
 
         // mGraphicBuffer points to the buffer allocated for this slot or is NULL
@@ -359,12 +352,6 @@
         // mCrop is the current crop rectangle for this buffer slot.
         Rect mCrop;
 
-        // mActiveRect is the current active rectangle for this buffer slot.
-        // Pixels outside of this rectangle are to be treated as completely
-        // transparent during window composition.  The rectangle is in buffer
-        // pixel coordinates.
-        Rect mActiveRect;
-
         // mTransform is the current transform flags for this buffer slot.
         uint32_t mTransform;
 
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index 7abc7db..1e33764 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -86,25 +86,21 @@
     // QueueBufferInput must be a POD structure
     struct QueueBufferInput {
         inline QueueBufferInput(int64_t timestamp,
-                const Rect& crop, int scalingMode, uint32_t transform,
-                const Rect& activeRect)
+                const Rect& crop, int scalingMode, uint32_t transform)
         : timestamp(timestamp), crop(crop), scalingMode(scalingMode),
-          transform(transform), activeRect(activeRect) { }
+          transform(transform) { }
         inline void deflate(int64_t* outTimestamp, Rect* outCrop,
-                int* outScalingMode, uint32_t* outTransform,
-                Rect* outActiveRect) const {
+                int* outScalingMode, uint32_t* outTransform) const {
             *outTimestamp = timestamp;
             *outCrop = crop;
             *outScalingMode = scalingMode;
             *outTransform = transform;
-            *outActiveRect = activeRect;
         }
     private:
         int64_t timestamp;
         Rect crop;
         int scalingMode;
         uint32_t transform;
-        Rect activeRect;
     };
 
     // QueueBufferOutput must be a POD structure
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 7540ed8..f9cf0be 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -144,6 +144,10 @@
     // updateTexImage() is called.
     status_t setDefaultBufferSize(uint32_t width, uint32_t height);
 
+    // setFilteringEnabled sets whether the transform matrix should be computed
+    // for use with bilinear filtering.
+    void setFilteringEnabled(bool enabled);
+
     // getCurrentBuffer returns the buffer associated with the current image.
     sp<GraphicBuffer> getCurrentBuffer() const;
 
@@ -154,9 +158,6 @@
     // getCurrentCrop returns the cropping rectangle of the current buffer.
     Rect getCurrentCrop() const;
 
-    // getCurrentActiveRect returns the active rectangle of the current buffer.
-    Rect getCurrentActiveRect() const;
-
     // getCurrentTransform returns the transform of the current buffer.
     uint32_t getCurrentTransform() const;
 
@@ -273,12 +274,6 @@
     // It gets set each time updateTexImage is called.
     Rect mCurrentCrop;
 
-    // mCurrentActiveRect is the active rectangle that applies to the current
-    // texture.  It gets set each time updateTexImage is called.  All pixels
-    // outside the active rectangle are be considered completely transparent for
-    // the purpose of window composition.
-    Rect mCurrentActiveRect;
-
     // mCurrentTransform is the transform identifier for the current texture. It
     // gets set each time updateTexImage is called.
     uint32_t mCurrentTransform;
@@ -298,6 +293,11 @@
 
     uint32_t mDefaultWidth, mDefaultHeight;
 
+    // mFilteringEnabled indicates whether the transform matrix is computed for
+    // use with bilinear filtering. It defaults to true and is changed by
+    // setFilteringEnabled().
+    bool mFilteringEnabled;
+
     // mTexName is the name of the OpenGL texture to which streamed images will
     // be bound when updateTexImage is called. It is set at construction time
     // and can be changed with a call to attachToContext.
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index c4d4320..b11d572 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -80,10 +80,10 @@
     int dispatchSetBuffersTransform(va_list args);
     int dispatchSetBuffersTimestamp(va_list args);
     int dispatchSetCrop(va_list args);
+    int dispatchSetPostTransformCrop(va_list args);
     int dispatchSetUsage(va_list args);
     int dispatchLock(va_list args);
     int dispatchUnlockAndPost(va_list args);
-    int dispatchSetActiveRect(va_list args);
 
 protected:
     virtual int cancelBuffer(ANativeWindowBuffer* buffer);
@@ -104,10 +104,10 @@
     virtual int setBuffersTransform(int transform);
     virtual int setBuffersTimestamp(int64_t timestamp);
     virtual int setCrop(Rect const* rect);
+    virtual int setPostTransformCrop(Rect const* rect);
     virtual int setUsage(uint32_t reqUsage);
     virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
     virtual int unlockAndPost();
-    virtual int setActiveRect(Rect const* rect);
 
     enum { NUM_BUFFER_SLOTS = BufferQueue::NUM_BUFFER_SLOTS };
     enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
@@ -159,6 +159,13 @@
     // that gets queued. It is set by calling setCrop.
     Rect mCrop;
 
+    // mCropNeedsTransform indicates whether mCrop is in post-transform
+    // coordinates and must be transformed using the inverse of mTransform
+    // before being queued with a buffer.  Otherwise the crop is passed
+    // untransformed.  It is initialized to false, is set to true by
+    // setPostTransformCrop, and set to false by setCrop.
+    bool mCropNeedsTransform;
+
     // mScalingMode is the scaling mode that will be used for the next
     // buffers that get queued. It is set by calling setScalingMode.
     int mScalingMode;
@@ -167,10 +174,6 @@
     // buffer that gets queued. It is set by calling setTransform.
     uint32_t mTransform;
 
-    // mActiveRect is the active rectangle that will be used for the next buffer
-    // that gets queued.  It is set by calling setActiveRect.
-    Rect mActiveRect;
-
      // mDefaultWidth is default width of the buffers, regardless of the
      // native_window_set_buffers_dimensions call.
      uint32_t mDefaultWidth;
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index 9e98bc5..bd82061 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -136,10 +136,18 @@
     void translate(int32_t dx, int32_t dy) { // legacy, don't use.
         offsetBy(dx, dy);
     }
- 
+
     Rect&   offsetTo(int32_t x, int32_t y);
     Rect&   offsetBy(int32_t x, int32_t y);
     bool    intersect(const Rect& with, Rect* result) const;
+
+    // Create a new Rect by transforming this one using a graphics HAL
+    // transform.  This rectangle is defined in a coordinate space starting at
+    // the origin and extending to (width, height).  If the transform includes
+    // a ROT90 then the output rectangle is defined in a space extending to
+    // (height, width).  Otherwise the output rectangle is in the same space as
+    // the input.
+    Rect transform(uint32_t xform, int32_t width, int32_t height);
 };
 
 ANDROID_BASIC_TYPES_TRAITS(Rect)
diff --git a/include/utils/Trace.h b/include/utils/Trace.h
index 6e2e284..984cd46 100644
--- a/include/utils/Trace.h
+++ b/include/utils/Trace.h
@@ -48,7 +48,9 @@
 #define ATRACE_TAG_WEBVIEW          (1<<4)
 #define ATRACE_TAG_WINDOW_MANAGER   (1<<5)
 #define ATRACE_TAG_ACTIVITY_MANAGER (1<<6)
-#define ATRACE_TAG_LAST             ATRACE_TAG_ACTIVITY_MANAGER
+#define ATRACE_TAG_SYNC_MANAGER     (1<<7)
+#define ATRACE_TAG_AUDIO            (1<<8)
+#define ATRACE_TAG_LAST             ATRACE_TAG_AUDIO
 
 #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
 
@@ -119,11 +121,16 @@
         }
     }
 
+    static void changeCallback();
+
     // init opens the trace marker file for writing and reads the
     // atrace.tags.enableflags system property.  It does this only the first
     // time it is run, using sMutex for synchronization.
     static void init();
 
+    // retrieve the current value of the system property.
+    static void loadSystemProperty();
+
     // sIsReady is a boolean value indicating whether a call to init() has
     // completed in this process.  It is initialized to 0 and set to 1 when the
     // first init() call completes.  It is set to 1 even if a failure occurred
diff --git a/include/utils/misc.h b/include/utils/misc.h
index 23f2a4c..d7d5bc1 100644
--- a/include/utils/misc.h
+++ b/include/utils/misc.h
@@ -88,6 +88,10 @@
 void k_itoa(int value, char* str, int base);
 char* itoa(int val, int base);
 
+typedef void (*sysprop_change_callback)(void);
+void add_sysprop_change_callback(sysprop_change_callback cb, int priority);
+void report_sysprop_change();
+
 }; // namespace android
 
 #endif // _LIBS_UTILS_MISC_H
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index e20d8a3..1f21f9c 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -17,6 +17,7 @@
 #include <binder/Binder.h>
 
 #include <utils/Atomic.h>
+#include <utils/misc.h>
 #include <binder/BpBinder.h>
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
@@ -199,6 +200,12 @@
             }
             return dump(fd, args);
         }
+
+        case SYSPROPS_TRANSACTION: {
+            report_sysprop_change();
+            return NO_ERROR;
+        }
+
         default:
             return UNKNOWN_TRANSACTION;
     }
diff --git a/libs/gui/BitTube.cpp b/libs/gui/BitTube.cpp
index 355a319..cf44bb9 100644
--- a/libs/gui/BitTube.cpp
+++ b/libs/gui/BitTube.cpp
@@ -140,8 +140,7 @@
         ssize_t size = tube->write(vaddr, objSize);
         if (size < 0) {
             // error occurred
-            numObjects = -size;
-            break;
+            return size;
         } else if (size == 0) {
             // no more space
             break;
@@ -160,8 +159,7 @@
         ssize_t size = tube->read(vaddr, objSize);
         if (size < 0) {
             // error occurred
-            numObjects = -size;
-            break;
+            return size;
         } else if (size == 0) {
             // no more messages
             break;
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 5095ebd..e53162b 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -540,15 +540,11 @@
     uint32_t transform;
     int scalingMode;
     int64_t timestamp;
-    Rect activeRect;
 
-    input.deflate(&timestamp, &crop, &scalingMode, &transform,
-            &activeRect);
+    input.deflate(&timestamp, &crop, &scalingMode, &transform);
 
-    ST_LOGV("queueBuffer: slot=%d time=%lld crop=[%d,%d,%d,%d] "
-            "active=[%d,%d,%d,%d]", buf, timestamp, crop.left, crop.top,
-            crop.right, crop.bottom, activeRect.left, activeRect.top,
-            activeRect.right, activeRect.bottom);
+    ST_LOGV("queueBuffer: slot=%d time=%lld crop=[%d,%d,%d,%d]",
+            buf, timestamp, crop.left, crop.top, crop.right, crop.bottom);
 
     sp<ConsumerListener> listener;
 
@@ -572,6 +568,16 @@
             return -EINVAL;
         }
 
+        const sp<GraphicBuffer>& graphicBuffer(mSlots[buf].mGraphicBuffer);
+        Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
+        Rect croppedCrop;
+        crop.intersect(bufferRect, &croppedCrop);
+        if (croppedCrop != crop) {
+            ST_LOGE("queueBuffer: crop rect is not contained within the "
+                    "buffer in slot %d", buf);
+            return -EINVAL;
+        }
+
         if (mSynchronousMode) {
             // In synchronous mode we queue all buffers in a FIFO.
             mQueue.push_back(buf);
@@ -600,12 +606,12 @@
         mSlots[buf].mTimestamp = timestamp;
         mSlots[buf].mCrop = crop;
         mSlots[buf].mTransform = transform;
-        mSlots[buf].mActiveRect = activeRect;
 
         switch (scalingMode) {
             case NATIVE_WINDOW_SCALING_MODE_FREEZE:
             case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
             case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+            case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
                 break;
             default:
                 ST_LOGE("unknown scaling mode: %d (ignoring)", scalingMode);
@@ -859,7 +865,6 @@
         buffer->mFrameNumber = mSlots[buf].mFrameNumber;
         buffer->mTimestamp = mSlots[buf].mTimestamp;
         buffer->mBuf = buf;
-        buffer->mActiveRect = mSlots[buf].mActiveRect;
         mSlots[buf].mAcquireCalled = true;
 
         mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
index f9a2c04..5cc76b4 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/gui/Sensor.cpp
@@ -40,6 +40,7 @@
 {
     mName = hwSensor->name;
     mVendor = hwSensor->vendor;
+    mVersion = hwSensor->version;
     mHandle = hwSensor->handle;
     mType = hwSensor->type;
     mMinValue = 0;                      // FIXME: minValue
@@ -101,7 +102,7 @@
 {
     return  sizeof(int32_t) + ((mName.length() + 3) & ~3) +
             sizeof(int32_t) + ((mVendor.length() + 3) & ~3) +
-            sizeof(int32_t) * 2 +
+            sizeof(int32_t) * 3 +
             sizeof(float) * 4 +
             sizeof(int32_t);
 }
@@ -140,6 +141,7 @@
     offset += write(buffer, offset, mName);
     offset += write(buffer, offset, int32_t(mVendor.length()));
     offset += write(buffer, offset, mVendor);
+    offset += write(buffer, offset, mVersion);
     offset += write(buffer, offset, mHandle);
     offset += write(buffer, offset, mType);
     offset += write(buffer, offset, mMinValue);
@@ -178,6 +180,7 @@
     offset += read(buffer, offset, &mName, len);
     offset += read(buffer, offset, &len);
     offset += read(buffer, offset, &mVendor, len);
+    offset += read(buffer, offset, &mVersion);
     offset += read(buffer, offset, &mHandle);
     offset += read(buffer, offset, &mType);
     offset += read(buffer, offset, &mMinValue);
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
index 04ba640..8a1bf41 100644
--- a/libs/gui/SensorEventQueue.cpp
+++ b/libs/gui/SensorEventQueue.cpp
@@ -79,14 +79,21 @@
     const int fd = getFd();
     sp<Looper> looper(getLooper());
 
+    int events;
     int32_t result;
     do {
-        result = looper->pollOnce(-1);
-        if (result == ALOOPER_EVENT_ERROR) {
+        result = looper->pollOnce(-1, NULL, &events, NULL);
+        if (result == ALOOPER_POLL_ERROR) {
             ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno);
             result = -EPIPE; // unknown error, so we make up one
             break;
         }
+        if (events & ALOOPER_EVENT_HANGUP) {
+            // the other-side has died
+            ALOGE("SensorEventQueue::waitForEvent error HANGUP");
+            result = -EPIPE; // unknown error, so we make up one
+            break;
+        }
     } while (result != fd);
 
     return  (result == fd) ? status_t(NO_ERROR) : result;
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index c103e14..1e58a21 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -108,6 +108,7 @@
         GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
     mCurrentTransform(0),
     mCurrentTimestamp(0),
+    mFilteringEnabled(true),
     mTexName(tex),
 #ifdef USE_FENCE_SYNC
     mUseFenceSync(useFenceSync),
@@ -294,7 +295,6 @@
         mCurrentTransform = item.mTransform;
         mCurrentScalingMode = item.mScalingMode;
         mCurrentTimestamp = item.mTimestamp;
-        mCurrentActiveRect = item.mActiveRect;
         computeCurrentTransformMatrix();
     } else  {
         if (err < 0) {
@@ -352,7 +352,7 @@
     // new EGLDisplay).
     for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
         EGLImageKHR img = mEGLSlots[i].mEglImage;
-        if (img != EGL_NO_IMAGE_KHR && i != mCurrentTexture) {
+        if (img != EGL_NO_IMAGE_KHR) {
             eglDestroyImageKHR(mEglDisplay, img);
             mEGLSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
         }
@@ -399,21 +399,12 @@
     glBindTexture(mTexTarget, tex);
 
     if (mCurrentTextureBuf != NULL) {
-        // If the current buffer is no longer associated with a slot, then it
-        // doesn't have an EGLImage.  In that case we create one now, but we also
-        // destroy it once we've used it to attach the buffer to the OpenGL ES
-        // texture.
-        bool imageNeedsDestroy = false;
-        EGLImageKHR image = EGL_NO_IMAGE_KHR;
-        if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
-            image = mEGLSlots[mCurrentTexture].mEglImage;
-            imageNeedsDestroy = false;
-        } else {
-            image = createImage(dpy, mCurrentTextureBuf);
-            if (image == EGL_NO_IMAGE_KHR) {
-                return UNKNOWN_ERROR;
-            }
-            imageNeedsDestroy = true;
+        // The EGLImageKHR that was associated with the slot was destroyed when
+        // the SurfaceTexture was detached from the old context, so we need to
+        // recreate it here.
+        EGLImageKHR image = createImage(dpy, mCurrentTextureBuf);
+        if (image == EGL_NO_IMAGE_KHR) {
+            return UNKNOWN_ERROR;
         }
 
         // Attach the current buffer to the GL texture.
@@ -427,9 +418,12 @@
             err = UNKNOWN_ERROR;
         }
 
-        if (imageNeedsDestroy) {
-            eglDestroyImageKHR(dpy, image);
-        }
+        // We destroy the EGLImageKHR here because the current buffer may no
+        // longer be associated with one of the buffer slots, so we have
+        // nowhere to to store it.  If the buffer is still associated with a
+        // slot then another EGLImageKHR will be created next time that buffer
+        // gets acquired in updateTexImage.
+        eglDestroyImageKHR(dpy, image);
 
         if (err != OK) {
             return err;
@@ -510,6 +504,15 @@
     memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
 }
 
+void SurfaceTexture::setFilteringEnabled(bool enabled) {
+    Mutex::Autolock lock(mMutex);
+    bool needsRecompute = mFilteringEnabled != enabled;
+    mFilteringEnabled = enabled;
+    if (needsRecompute) {
+        computeCurrentTransformMatrix();
+    }
+}
+
 void SurfaceTexture::computeCurrentTransformMatrix() {
     ST_LOGV("computeCurrentTransformMatrix");
 
@@ -540,37 +543,50 @@
     }
 
     sp<GraphicBuffer>& buf(mCurrentTextureBuf);
-    float tx, ty, sx, sy;
-    if (!mCurrentCrop.isEmpty()) {
-        // In order to prevent bilinear sampling at the of the crop rectangle we
-        // may need to shrink it by 2 texels in each direction.  Normally this
-        // would just need to take 1/2 a texel off each end, but because the
-        // chroma channels will likely be subsampled we need to chop off a whole
-        // texel.  This will cause artifacts if someone does nearest sampling
-        // with 1:1 pixel:texel ratio, but it's impossible to simultaneously
-        // accomodate the bilinear and nearest sampling uses.
-        //
-        // If nearest sampling turns out to be a desirable usage of these
-        // textures then we could add the ability to switch a SurfaceTexture to
-        // nearest-mode.  Preferably, however, the image producers (video
-        // decoder, camera, etc.) would simply not use a crop rectangle (or at
-        // least not tell the framework about it) so that the GPU can do the
-        // correct edge behavior.
-        const float shrinkAmount = 1.0f; // the amount that each edge is shrunk
+    Rect cropRect = mCurrentCrop;
+    float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
+    float bufferWidth = buf->getWidth();
+    float bufferHeight = buf->getHeight();
+    if (!cropRect.isEmpty()) {
+        float shrinkAmount = 0.0f;
+        if (mFilteringEnabled) {
+            // In order to prevent bilinear sampling beyond the edge of the
+            // crop rectangle we may need to shrink it by 2 texels in each
+            // dimension.  Normally this would just need to take 1/2 a texel
+            // off each end, but because the chroma channels of YUV420 images
+            // are subsampled we may need to shrink the crop region by a whole
+            // texel on each side.
+            switch (buf->getPixelFormat()) {
+                case PIXEL_FORMAT_RGBA_8888:
+                case PIXEL_FORMAT_RGBX_8888:
+                case PIXEL_FORMAT_RGB_888:
+                case PIXEL_FORMAT_RGB_565:
+                case PIXEL_FORMAT_BGRA_8888:
+                case PIXEL_FORMAT_RGBA_5551:
+                case PIXEL_FORMAT_RGBA_4444:
+                    // We know there's no subsampling of any channels, so we
+                    // only need to shrink by a half a pixel.
+                    shrinkAmount = 0.5;
 
-        tx = (float(mCurrentCrop.left) + shrinkAmount) /
-                float(buf->getWidth());
-        ty = (float(buf->getHeight() - mCurrentCrop.bottom) +
-                shrinkAmount) / float(buf->getHeight());
-        sx = (float(mCurrentCrop.width()) - (2.0f * shrinkAmount)) /
-                float(buf->getWidth());
-        sy = (float(mCurrentCrop.height()) - (2.0f * shrinkAmount)) /
-                float(buf->getHeight());
-    } else {
-        tx = 0.0f;
-        ty = 0.0f;
-        sx = 1.0f;
-        sy = 1.0f;
+                default:
+                    // If we don't recognize the format, we must assume the
+                    // worst case (that we care about), which is YUV420.
+                    shrinkAmount = 1.0;
+            }
+        }
+
+        // Only shrink the dimensions that are not the size of the buffer.
+        if (cropRect.width() < bufferWidth) {
+            tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+            sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
+                    bufferWidth;
+        }
+        if (cropRect.height() < bufferHeight) {
+            ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
+                    bufferHeight;
+            sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
+                    bufferHeight;
+        }
     }
     float crop[16] = {
         sx, 0, 0, 0,
@@ -659,11 +675,6 @@
     return outCrop;
 }
 
-Rect SurfaceTexture::getCurrentActiveRect() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentActiveRect;
-}
-
 uint32_t SurfaceTexture::getCurrentTransform() const {
     Mutex::Autolock lock(mMutex);
     return mCurrentTransform;
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index fdd14c8..9c3e28d 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -76,9 +76,9 @@
     mReqUsage = 0;
     mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
     mCrop.clear();
+    mCropNeedsTransform = false;
     mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
     mTransform = 0;
-    mActiveRect.clear();
     mDefaultWidth = 0;
     mDefaultHeight = 0;
     mUserWidth = 0;
@@ -238,9 +238,29 @@
         return i;
     }
 
+    Rect crop(mCrop);
+    if (mCropNeedsTransform) {
+        // The crop rect was specified in the post-transform coordinate space,
+        // so we need to transform that rect by the inverse of mTransform to
+        // put it into the buffer pixel space before queuing it.
+        uint32_t invTransform = mTransform;
+        int32_t width = buffer->width;
+        int32_t height = buffer->height;
+        if (mTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+            invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
+                    NATIVE_WINDOW_TRANSFORM_FLIP_H;
+            width = buffer->height;
+            height = buffer->width;
+        }
+        crop = mCrop.transform(invTransform, width, height);
+    }
+
+    // Make sure the crop rectangle is entirely inside the buffer.
+    crop.intersect(Rect(buffer->width, buffer->height), &crop);
+
     ISurfaceTexture::QueueBufferOutput output;
-    ISurfaceTexture::QueueBufferInput input(timestamp,
-            mCrop, mScalingMode, mTransform, mActiveRect);
+    ISurfaceTexture::QueueBufferInput input(timestamp, crop, mScalingMode,
+            mTransform);
     status_t err = mSurfaceTexture->queueBuffer(i, input, &output);
     if (err != OK)  {
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
@@ -321,6 +341,9 @@
     case NATIVE_WINDOW_SET_CROP:
         res = dispatchSetCrop(args);
         break;
+    case NATIVE_WINDOW_SET_POST_TRANSFORM_CROP:
+        res = dispatchSetPostTransformCrop(args);
+        break;
     case NATIVE_WINDOW_SET_BUFFER_COUNT:
         res = dispatchSetBufferCount(args);
         break;
@@ -357,9 +380,6 @@
     case NATIVE_WINDOW_API_DISCONNECT:
         res = dispatchDisconnect(args);
         break;
-    case NATIVE_WINDOW_SET_ACTIVE_RECT:
-        res = dispatchSetActiveRect(args);
-        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -387,6 +407,11 @@
     return setCrop(reinterpret_cast<Rect const*>(rect));
 }
 
+int SurfaceTextureClient::dispatchSetPostTransformCrop(va_list args) {
+    android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
+    return setPostTransformCrop(reinterpret_cast<Rect const*>(rect));
+}
+
 int SurfaceTextureClient::dispatchSetBufferCount(va_list args) {
     size_t bufferCount = va_arg(args, size_t);
     return setBufferCount(bufferCount);
@@ -430,11 +455,6 @@
     return setBuffersTransform(transform);
 }
 
-int SurfaceTextureClient::dispatchSetActiveRect(va_list args) {
-    android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
-    return setActiveRect(reinterpret_cast<Rect const*>(rect));
-}
-
 int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) {
     int64_t timestamp = va_arg(args, int64_t);
     return setBuffersTimestamp(timestamp);
@@ -481,6 +501,7 @@
         mReqHeight = 0;
         mReqUsage = 0;
         mCrop.clear();
+        mCropNeedsTransform = false;
         mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
         mTransform = 0;
         if (api == NATIVE_WINDOW_API_CPU) {
@@ -512,6 +533,25 @@
 
     Mutex::Autolock lock(mMutex);
     mCrop = realRect;
+    mCropNeedsTransform = false;
+    return NO_ERROR;
+}
+
+int SurfaceTextureClient::setPostTransformCrop(Rect const* rect)
+{
+    ATRACE_CALL();
+    ALOGV("SurfaceTextureClient::setPostTransformCrop");
+
+    Rect realRect;
+    if (rect == NULL || rect->isEmpty()) {
+        realRect.clear();
+    } else {
+        realRect = *rect;
+    }
+
+    Mutex::Autolock lock(mMutex);
+    mCrop = realRect;
+    mCropNeedsTransform = true;
     return NO_ERROR;
 }
 
@@ -546,7 +586,6 @@
     Mutex::Autolock lock(mMutex);
     mReqWidth = w;
     mReqHeight = h;
-    mCrop.clear();
     return NO_ERROR;
 }
 
@@ -564,7 +603,6 @@
     Mutex::Autolock lock(mMutex);
     mUserWidth = w;
     mUserHeight = h;
-    mCrop.clear();
     return NO_ERROR;
 }
 
@@ -589,6 +627,7 @@
         case NATIVE_WINDOW_SCALING_MODE_FREEZE:
         case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
         case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+        case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
             break;
         default:
             ALOGE("unknown scaling mode: %d", mode);
@@ -609,23 +648,6 @@
     return NO_ERROR;
 }
 
-int SurfaceTextureClient::setActiveRect(Rect const* rect)
-{
-    ATRACE_CALL();
-    ALOGV("SurfaceTextureClient::setActiveRect");
-
-    Rect realRect;
-    if (rect == NULL || rect->isEmpty()) {
-        realRect.clear();
-    } else {
-        realRect = *rect;
-    }
-
-    Mutex::Autolock lock(mMutex);
-    mActiveRect = realRect;
-    return NO_ERROR;
-}
-
 int SurfaceTextureClient::setBuffersTimestamp(int64_t timestamp)
 {
     ALOGV("SurfaceTextureClient::setBuffersTimestamp");
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index b576ca5..8546fb9 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -20,6 +20,7 @@
 #include <EGL/egl.h>
 #include <gtest/gtest.h>
 #include <gui/SurfaceTextureClient.h>
+#include <system/graphics.h>
 #include <utils/Log.h>
 #include <utils/Thread.h>
 
@@ -441,6 +442,68 @@
     ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2]));
 }
 
+TEST_F(SurfaceTextureClientTest, SetPostTransformCropUntransforms) {
+    android_native_rect_t rect = {1, 5, 4, 14};
+    native_window_set_post_transform_crop(mANW.get(), &rect);
+
+    uint32_t xforms[] = {
+        HAL_TRANSFORM_FLIP_H,
+        HAL_TRANSFORM_FLIP_V,
+        HAL_TRANSFORM_ROT_90,
+        HAL_TRANSFORM_ROT_180,
+        HAL_TRANSFORM_ROT_270,
+    };
+
+    Rect expectedRects[] = {
+        Rect(4, 5, 7, 14), // HAL_TRANSFORM_FLIP_H
+        Rect(1, 2, 4, 11), // HAL_TRANSFORM_FLIP_V
+        Rect(5, 4, 14, 7), // HAL_TRANSFORM_ROT_90
+        Rect(4, 2, 7, 11), // HAL_TRANSFORM_ROT_180
+        Rect(2, 1, 11, 4), // HAL_TRANSFORM_ROT_270
+    };
+
+    for (size_t i = 0; i < sizeof(xforms)/sizeof(xforms[0]); i++) {
+        SCOPED_TRACE(String8::format("xform=%#x", xforms[i]).string());
+
+        int w = 8, h = 16;
+        if (xforms[i] & HAL_TRANSFORM_ROT_90) {
+            w = 16;
+            h = 8;
+        }
+        ASSERT_EQ(OK, native_window_set_buffers_transform(mANW.get(), xforms[i]));
+        ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), w, h));
+
+        android_native_buffer_t* buf;
+        ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf));
+        ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf));
+        ASSERT_EQ(OK, mST->updateTexImage());
+
+        Rect crop = mST->getCurrentCrop();
+        EXPECT_EQ(expectedRects[i].left, crop.left);
+        EXPECT_EQ(expectedRects[i].top, crop.top);
+        EXPECT_EQ(expectedRects[i].right, crop.right);
+        EXPECT_EQ(expectedRects[i].bottom, crop.bottom);
+    }
+}
+
+TEST_F(SurfaceTextureClientTest, SetCropCropsCrop) {
+    android_native_rect_t rect = {-2, -13, 40, 18};
+    native_window_set_crop(mANW.get(), &rect);
+
+    ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 4, 4));
+
+    android_native_buffer_t* buf;
+    ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf));
+    ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf));
+    ASSERT_EQ(OK, mST->updateTexImage());
+
+    Rect crop = mST->getCurrentCrop();
+    EXPECT_EQ(0, crop.left);
+    EXPECT_EQ(0, crop.top);
+    EXPECT_EQ(4, crop.right);
+    EXPECT_EQ(4, crop.bottom);
+}
+
 // XXX: This is not expected to pass until the synchronization hacks are removed
 // from the SurfaceTexture class.
 TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) {
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index cff4476..d708f6d 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -1262,28 +1262,6 @@
     native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU);
 }
 
-TEST_F(SurfaceTextureGLTest, GetCurrentActiveRectWorks) {
-    ASSERT_EQ(OK, mST->setSynchronousMode(true));
-
-    ASSERT_EQ(OK, native_window_api_connect(mANW.get(),
-            NATIVE_WINDOW_API_CPU));
-
-    ANativeWindowBuffer *anb;
-
-    android_native_rect_t odd = {23, 78, 123, 477};
-    ASSERT_EQ(OK, native_window_set_active_rect(mANW.get(), &odd));
-    EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb));
-    EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb));
-    mFW->waitForFrame();
-    EXPECT_EQ(OK,mST->updateTexImage());
-    Rect r = mST->getCurrentCrop();
-    assertRectEq(Rect(23, 78, 123, 477), r);
-
-    ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(),
-            NATIVE_WINDOW_API_CPU));
-}
-
-
 TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
     class ProducerThread : public Thread {
     public:
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index 5694e00..65fe5f9 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <system/graphics.h>
 #include <ui/Rect.h>
 
 namespace android {
@@ -92,4 +93,24 @@
     return !(result->isEmpty());
 }
 
+Rect Rect::transform(uint32_t xform, int32_t width, int32_t height) {
+    Rect result(*this);
+    if (xform & HAL_TRANSFORM_FLIP_H) {
+        result = Rect(width - result.right, result.top,
+                width - result.left, result.bottom);
+    }
+    if (xform & HAL_TRANSFORM_FLIP_V) {
+        result = Rect(result.left, height - result.bottom,
+                result.right, height - result.top);
+    }
+    if (xform & HAL_TRANSFORM_ROT_90) {
+        int left = height - result.bottom;
+        int top = result.left;
+        int right = height - result.top;
+        int bottom = result.right;
+        result = Rect(left, top, right, bottom);
+    }
+    return result;
+}
+
 }; // namespace android
diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp
index f7d8abe..5cd5731 100644
--- a/libs/utils/Trace.cpp
+++ b/libs/utils/Trace.cpp
@@ -19,6 +19,7 @@
 #include <cutils/properties.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
+#include <utils/misc.h>
 
 namespace android {
 
@@ -27,10 +28,19 @@
 uint64_t Tracer::sEnabledTags = 0;
 Mutex Tracer::sMutex;
 
+void Tracer::changeCallback() {
+    Mutex::Autolock lock(sMutex);
+    if (sIsReady && sTraceFD >= 0) {
+        loadSystemProperty();
+    }
+}
+
 void Tracer::init() {
     Mutex::Autolock lock(sMutex);
 
     if (!sIsReady) {
+        add_sysprop_change_callback(changeCallback, 0);
+
         const char* const traceFileName =
                 "/sys/kernel/debug/tracing/trace_marker";
         sTraceFD = open(traceFileName, O_WRONLY);
@@ -38,14 +48,18 @@
             ALOGE("error opening trace file: %s (%d)", strerror(errno), errno);
             // sEnabledTags remains zero indicating that no tracing can occur
         } else {
-            char value[PROPERTY_VALUE_MAX];
-            property_get("atrace.tags.enableflags", value, "0");
-            sEnabledTags = (strtoll(value, NULL, 0) & ATRACE_TAG_VALID_MASK)
-                    | ATRACE_TAG_ALWAYS;
+            loadSystemProperty();
         }
 
         android_atomic_release_store(1, &sIsReady);
     }
 }
 
+void Tracer::loadSystemProperty() {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.atrace.tags.enableflags", value, "0");
+    sEnabledTags = (strtoll(value, NULL, 0) & ATRACE_TAG_VALID_MASK)
+            | ATRACE_TAG_ALWAYS;
+}
+
 } // namespace andoid
diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp
index dc89d15..b3c99e6 100644
--- a/libs/utils/misc.cpp
+++ b/libs/utils/misc.cpp
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "misc"
+
 //
 // Miscellaneous utility functions.
 //
 #include <utils/misc.h>
+#include <utils/Log.h>
 
 #include <sys/stat.h>
 #include <string.h>
@@ -25,6 +28,12 @@
 #include <assert.h>
 #include <stdio.h>
 
+#if defined(HAVE_PTHREADS)
+# include <pthread.h>
+#endif
+
+#include <utils/Vector.h>
+
 using namespace android;
 
 namespace android {
@@ -181,5 +190,54 @@
     return val;
 }
 
-}; // namespace android
+struct sysprop_change_callback_info {
+    sysprop_change_callback callback;
+    int priority;
+};
 
+#if defined(HAVE_PTHREADS)
+static pthread_mutex_t gSyspropMutex = PTHREAD_MUTEX_INITIALIZER;
+static Vector<sysprop_change_callback_info>* gSyspropList = NULL;
+#endif
+
+void add_sysprop_change_callback(sysprop_change_callback cb, int priority) {
+#if defined(HAVE_PTHREADS)
+    pthread_mutex_lock(&gSyspropMutex);
+    if (gSyspropList == NULL) {
+        gSyspropList = new Vector<sysprop_change_callback_info>();
+    }
+    sysprop_change_callback_info info;
+    info.callback = cb;
+    info.priority = priority;
+    bool added = false;
+    for (size_t i=0; i<gSyspropList->size(); i++) {
+        if (priority >= gSyspropList->itemAt(i).priority) {
+            gSyspropList->insertAt(info, i);
+            added = true;
+            break;
+        }
+    }
+    if (!added) {
+        gSyspropList->add(info);
+    }
+    pthread_mutex_unlock(&gSyspropMutex);
+#endif
+}
+
+void report_sysprop_change() {
+#if defined(HAVE_PTHREADS)
+    pthread_mutex_lock(&gSyspropMutex);
+    Vector<sysprop_change_callback_info> listeners;
+    if (gSyspropList != NULL) {
+        listeners = *gSyspropList;
+    }
+    pthread_mutex_unlock(&gSyspropMutex);
+
+    //ALOGI("Reporting sysprop change to %d listeners", listeners.size());
+    for (size_t i=0; i<listeners.size(); i++) {
+        listeners[i].callback();
+    }
+#endif
+}
+
+}; // namespace android
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 66bc64d..c5c2618 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -36,6 +36,10 @@
 LOCAL_CFLAGS += -fvisibility=hidden
 LOCAL_CFLAGS += -DEGL_TRACE=1
 
+ifeq ($(BOARD_ALLOW_EGL_HIBERNATION),true)
+  LOCAL_CFLAGS += -DBOARD_ALLOW_EGL_HIBERNATION
+endif
+
 ifeq ($(TARGET_BOARD_PLATFORM),msm7k)
 LOCAL_CFLAGS += -DADRENO130=1
 endif
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index f2e3897..2cd1a93 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -420,7 +420,7 @@
     if (mWakeCount == 0 && CC_UNLIKELY(mAttemptHibernation)) {
         egl_connection_t* const cnx = &gEGLImpl;
         mAttemptHibernation = false;
-        if (mDpyValid &&
+        if (mAllowHibernation && mDpyValid &&
                 cnx->egl.eglHibernateProcessIMG &&
                 cnx->egl.eglAwakenProcessIMG) {
             ALOGV("Hibernating\n");
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 412568b..7bb09a3 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -155,7 +155,12 @@
         };
 
         HibernationMachine(): mWakeCount(0), mHibernating(false),
-                mAttemptHibernation(false), mDpyValid(false)
+                mAttemptHibernation(false), mDpyValid(false),
+#if BOARD_ALLOW_EGL_HIBERNATION
+                mAllowHibernation(true)
+#else
+                mAllowHibernation(false)
+#endif
         {}
         ~HibernationMachine() {}
 
@@ -165,11 +170,12 @@
         void setDisplayValid(bool valid);
 
     private:
-        Mutex   mLock;
-        int32_t mWakeCount;
-        bool    mHibernating;
-        bool    mAttemptHibernation;
-        bool    mDpyValid;
+        Mutex      mLock;
+        int32_t    mWakeCount;
+        bool       mHibernating;
+        bool       mAttemptHibernation;
+        bool       mDpyValid;
+        const bool mAllowHibernation;
     };
     HibernationMachine mHibernation;
 };
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e15e733..e936188 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -141,6 +141,47 @@
 void Layer::validateVisibility(const Transform& globalTransform) {
     LayerBase::validateVisibility(globalTransform);
 
+    if (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE &&
+            !mCurrentCrop.isEmpty()) {
+        // We need to shrink the window size to match the buffer crop
+        // rectangle.
+        const Layer::State& s(drawingState());
+        const Transform tr(globalTransform * s.transform);
+        float windowWidth = s.w;
+        float windowHeight = s.h;
+        float bufferWidth = mActiveBuffer->getWidth();
+        float bufferHeight = mActiveBuffer->getHeight();
+        if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+            float tmp = bufferWidth;
+            bufferWidth = bufferHeight;
+            bufferHeight = tmp;
+        }
+        float xScale = float(windowWidth) / float(bufferWidth);
+        float yScale = float(windowHeight) / float(bufferHeight);
+
+        // Compute the crop in post-transform coordinates.
+        Rect crop(mCurrentCrop.transform(mCurrentTransform,
+                    mActiveBuffer->getWidth(), mActiveBuffer->getHeight()));
+
+        float left = ceil(xScale * float(crop.left));
+        float right = floor(xScale * float(crop.right));
+        float top = ceil(yScale * float(crop.top));
+        float bottom = floor(yScale * float(crop.bottom));
+
+        tr.transform(mVertices[0], left, top);
+        tr.transform(mVertices[1], left, bottom);
+        tr.transform(mVertices[2], right, bottom);
+        tr.transform(mVertices[3], right, top);
+
+        const DisplayHardware& hw(graphicPlane(0).displayHardware());
+        const uint32_t hw_h = hw.getHeight();
+        for (size_t i=0 ; i<4 ; i++)
+            mVertices[i][1] = hw_h - mVertices[i][1];
+
+        mTransformedBounds = tr.transform(
+                Rect(int(left), int(top), int(right), int(bottom)));
+    }
+
     // This optimization allows the SurfaceTexture to bake in
     // the rotation so hardware overlays can be used
     mSurfaceTexture->setTransformHint(getTransformHint());
@@ -317,16 +358,24 @@
     }
 
     if (!isProtected()) {
+        // TODO: we could be more subtle with isFixedSize()
+        const bool useFiltering = getFiltering() || needsFiltering() || isFixedSize();
+
+        // Query the texture matrix given our current filtering mode.
+        float textureMatrix[16];
+        mSurfaceTexture->setFilteringEnabled(useFiltering);
+        mSurfaceTexture->getTransformMatrix(textureMatrix);
+
+        // Set things up for texturing.
         glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureName);
         GLenum filter = GL_NEAREST;
-        if (getFiltering() || needsFiltering() || isFixedSize() || isCropped()) {
-            // TODO: we could be more subtle with isFixedSize()
+        if (useFiltering) {
             filter = GL_LINEAR;
         }
         glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, filter);
         glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, filter);
         glMatrixMode(GL_TEXTURE);
-        glLoadMatrixf(mTextureMatrix);
+        glLoadMatrixf(textureMatrix);
         glMatrixMode(GL_MODELVIEW);
         glDisable(GL_TEXTURE_2D);
         glEnable(GL_TEXTURE_EXTERNAL_OES);
@@ -481,7 +530,7 @@
             mFlinger->invalidateHwcGeometry();
         }
 
-        const Rect crop(mSurfaceTexture->getCurrentCrop());
+        Rect crop(mSurfaceTexture->getCurrentCrop());
         const uint32_t transform(mSurfaceTexture->getCurrentTransform());
         const uint32_t scalingMode(mSurfaceTexture->getCurrentScalingMode());
         if ((crop != mCurrentCrop) ||
@@ -494,13 +543,6 @@
             mFlinger->invalidateHwcGeometry();
         }
 
-        GLfloat textureMatrix[16];
-        mSurfaceTexture->getTransformMatrix(textureMatrix);
-        if (memcmp(textureMatrix, mTextureMatrix, sizeof(textureMatrix))) {
-            memcpy(mTextureMatrix, textureMatrix, sizeof(textureMatrix));
-            mFlinger->invalidateHwcGeometry();
-        }
-
         uint32_t bufWidth  = mActiveBuffer->getWidth();
         uint32_t bufHeight = mActiveBuffer->getHeight();
         if (oldActiveBuffer != NULL) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 9a04848..1188621 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -114,7 +114,6 @@
 
     // main thread
     sp<GraphicBuffer> mActiveBuffer;
-    GLfloat mTextureMatrix[16];
     Rect mCurrentCrop;
     uint32_t mCurrentTransform;
     uint32_t mCurrentScalingMode;
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index ec74243..4fe261a 100644
--- a/services/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -77,6 +77,7 @@
             Rect    makeBounds(int w, int h) const;
             void    transform(float* point, int x, int y) const;
             Region  transform(const Region& reg) const;
+            Rect    transform(const Rect& bounds) const;
             Transform operator * (const Transform& rhs) const;
 
             // for debugging
@@ -112,7 +113,6 @@
     // assumes the last row is < 0 , 0 , 1 >
     vec2 transform(const vec2& v) const;
     vec3 transform(const vec3& v) const;
-    Rect transform(const Rect& bounds) const;
     uint32_t type() const;
     static bool absIsOne(float f);
     static bool isZero(float f);