Add sticky transform to surfaceflinger.

Bug: 15116722

- Adds a sticky transform field that can be set from a
  SurfaceFlinger client Surface.  This transform is
  added to any transform applied to the Surface.

Change-Id: Idaa4311dfd027b2d2b8ea5e2c6cba2da5779d753
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index fe8a308..3fc5de2 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -195,6 +195,8 @@
     // most updates).
     String8 mConsumerName;
 
+    uint32_t mStickyTransform;
+
 }; // class BufferQueueProducer
 
 } // namespace android
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 9b96b2b..4e9e810 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -273,15 +273,18 @@
         // async - if the buffer is queued in asynchronous mode
         // fence - a fence that the consumer must wait on before reading the buffer,
         //         set this to Fence::NO_FENCE if the buffer is ready immediately
+        // sticky - the sticky transform set in Surface (only used by the LEGACY
+        //          camera mode).
         inline QueueBufferInput(int64_t timestamp, bool isAutoTimestamp,
                 const Rect& crop, int scalingMode, uint32_t transform, bool async,
-                const sp<Fence>& fence)
+                const sp<Fence>& fence, uint32_t sticky = 0)
         : timestamp(timestamp), isAutoTimestamp(isAutoTimestamp), crop(crop),
-          scalingMode(scalingMode), transform(transform), async(async),
-          fence(fence) { }
+          scalingMode(scalingMode), transform(transform), stickyTransform(sticky),
+          async(async), fence(fence) { }
         inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp,
                 Rect* outCrop, int* outScalingMode, uint32_t* outTransform,
-                bool* outAsync, sp<Fence>* outFence) const {
+                bool* outAsync, sp<Fence>* outFence,
+                uint32_t* outStickyTransform = NULL) const {
             *outTimestamp = timestamp;
             *outIsAutoTimestamp = bool(isAutoTimestamp);
             *outCrop = crop;
@@ -289,6 +292,9 @@
             *outTransform = transform;
             *outAsync = bool(async);
             *outFence = fence;
+            if (outStickyTransform != NULL) {
+                *outStickyTransform = stickyTransform;
+            }
         }
 
         // Flattenable protocol
@@ -303,6 +309,7 @@
         Rect crop;
         int scalingMode;
         uint32_t transform;
+        uint32_t stickyTransform;
         int async;
         sp<Fence> fence;
     };
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index dcfe74f..35ab7f6 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -138,6 +138,7 @@
     int dispatchSetBuffersFormat(va_list args);
     int dispatchSetScalingMode(va_list args);
     int dispatchSetBuffersTransform(va_list args);
+    int dispatchSetBuffersStickyTransform(va_list args);
     int dispatchSetBuffersTimestamp(va_list args);
     int dispatchSetCrop(va_list args);
     int dispatchSetPostTransformCrop(va_list args);
@@ -163,6 +164,7 @@
     virtual int setBuffersFormat(int format);
     virtual int setScalingMode(int mode);
     virtual int setBuffersTransform(int transform);
+    virtual int setBuffersStickyTransform(int transform);
     virtual int setBuffersTimestamp(int64_t timestamp);
     virtual int setCrop(Rect const* rect);
     virtual int setUsage(uint32_t reqUsage);
@@ -231,6 +233,12 @@
     // buffer that gets queued. It is set by calling setTransform.
     uint32_t mTransform;
 
+    // mStickyTransform is a transform that is applied on top of mTransform
+    // in each buffer that is queued.  This is typically used to force the
+    // compositor to apply a transform, and will prevent the transform hint
+    // from being set by the compositor.
+    uint32_t mStickyTransform;
+
      // mDefaultWidth is default width of the buffers, regardless of the
      // native_window_set_buffers_dimensions call.
      uint32_t mDefaultWidth;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 70c3ff3..6feebf7 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -37,7 +37,8 @@
 BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
     mCore(core),
     mSlots(core->mSlots),
-    mConsumerName() {}
+    mConsumerName(),
+    mStickyTransform(0) {}
 
 BufferQueueProducer::~BufferQueueProducer() {}
 
@@ -509,10 +510,11 @@
     Rect crop;
     int scalingMode;
     uint32_t transform;
+    uint32_t stickyTransform;
     bool async;
     sp<Fence> fence;
     input.deflate(&timestamp, &isAutoTimestamp, &crop, &scalingMode, &transform,
-            &async, &fence);
+            &async, &fence, &stickyTransform);
 
     if (fence == NULL) {
         BQ_LOGE("queueBuffer: fence is NULL");
@@ -601,6 +603,8 @@
         item.mFence = fence;
         item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async;
 
+        mStickyTransform = stickyTransform;
+
         if (mCore->mQueue.empty()) {
             // When the queue is empty, we can ignore mDequeueBufferCannotBlock
             // and simply queue this buffer
@@ -701,6 +705,9 @@
         case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
             value = mCore->getMinUndequeuedBufferCountLocked(false);
             break;
+        case NATIVE_WINDOW_STICKY_TRANSFORM:
+            value = static_cast<int>(mStickyTransform);
+            break;
         case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
             value = (mCore->mQueue.size() > 1);
             break;
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 8d9a800..1e28f9b 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -435,6 +435,7 @@
          + sizeof(crop)
          + sizeof(scalingMode)
          + sizeof(transform)
+         + sizeof(stickyTransform)
          + sizeof(async)
          + fence->getFlattenedSize();
 }
@@ -454,6 +455,7 @@
     FlattenableUtils::write(buffer, size, crop);
     FlattenableUtils::write(buffer, size, scalingMode);
     FlattenableUtils::write(buffer, size, transform);
+    FlattenableUtils::write(buffer, size, stickyTransform);
     FlattenableUtils::write(buffer, size, async);
     return fence->flatten(buffer, size, fds, count);
 }
@@ -467,6 +469,7 @@
             + sizeof(crop)
             + sizeof(scalingMode)
             + sizeof(transform)
+            + sizeof(stickyTransform)
             + sizeof(async);
 
     if (size < minNeeded) {
@@ -478,6 +481,7 @@
     FlattenableUtils::read(buffer, size, crop);
     FlattenableUtils::read(buffer, size, scalingMode);
     FlattenableUtils::read(buffer, size, transform);
+    FlattenableUtils::read(buffer, size, stickyTransform);
     FlattenableUtils::read(buffer, size, async);
 
     fence = new Fence();
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 8cb9189..86451be 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -66,6 +66,7 @@
     mCrop.clear();
     mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
     mTransform = 0;
+    mStickyTransform = 0;
     mDefaultWidth = 0;
     mDefaultHeight = 0;
     mUserWidth = 0;
@@ -315,15 +316,22 @@
     sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
     IGraphicBufferProducer::QueueBufferOutput output;
     IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
-            crop, mScalingMode, mTransform, mSwapIntervalZero, fence);
+            crop, mScalingMode, mTransform ^ mStickyTransform, mSwapIntervalZero,
+            fence, mStickyTransform);
     status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
     if (err != OK)  {
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
     }
     uint32_t numPendingBuffers = 0;
-    output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
+    uint32_t hint = 0;
+    output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
             &numPendingBuffers);
 
+    // Disable transform hint if sticky transform is set.
+    if (mStickyTransform == 0) {
+        mTransformHint = hint;
+    }
+
     mConsumerRunningBehind = (numPendingBuffers >= 2);
 
     return err;
@@ -405,6 +413,9 @@
     case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
         res = dispatchSetBuffersTransform(args);
         break;
+    case NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM:
+        res = dispatchSetBuffersStickyTransform(args);
+        break;
     case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
         res = dispatchSetBuffersTimestamp(args);
         break;
@@ -502,6 +513,11 @@
     return setBuffersTransform(transform);
 }
 
+int Surface::dispatchSetBuffersStickyTransform(va_list args) {
+    int transform = va_arg(args, int);
+    return setBuffersStickyTransform(transform);
+}
+
 int Surface::dispatchSetBuffersTimestamp(va_list args) {
     int64_t timestamp = va_arg(args, int64_t);
     return setBuffersTimestamp(timestamp);
@@ -527,8 +543,15 @@
     int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
     if (err == NO_ERROR) {
         uint32_t numPendingBuffers = 0;
-        output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint,
+        uint32_t hint = 0;
+        output.deflate(&mDefaultWidth, &mDefaultHeight, &hint,
                 &numPendingBuffers);
+
+        // Disable transform hint if sticky transform is set.
+        if (mStickyTransform == 0) {
+            mTransformHint = hint;
+        }
+
         mConsumerRunningBehind = (numPendingBuffers >= 2);
     }
     if (!err && api == NATIVE_WINDOW_API_CPU) {
@@ -552,6 +575,8 @@
         mCrop.clear();
         mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
         mTransform = 0;
+        mStickyTransform = 0;
+
         if (api == NATIVE_WINDOW_API_CPU) {
             mConnectedToCpu = false;
         }
@@ -678,6 +703,15 @@
     return NO_ERROR;
 }
 
+int Surface::setBuffersStickyTransform(int transform)
+{
+    ATRACE_CALL();
+    ALOGV("Surface::setBuffersStickyTransform");
+    Mutex::Autolock lock(mMutex);
+    mStickyTransform = transform;
+    return NO_ERROR;
+}
+
 int Surface::setBuffersTimestamp(int64_t timestamp)
 {
     ALOGV("Surface::setBuffersTimestamp");
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 63bc257..4861e34 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -624,6 +624,17 @@
     engine.disableBlending();
 }
 
+uint32_t Layer::getProducerStickyTransform() const {
+    int producerStickyTransform = 0;
+    int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform);
+    if (ret != OK) {
+        ALOGW("%s: Error %s (%d) while querying window sticky transform.", __FUNCTION__,
+                strerror(-ret), ret);
+        return 0;
+    }
+    return static_cast<uint32_t>(producerStickyTransform);
+}
+
 void Layer::setFiltering(bool filtering) {
     mFiltering = filtering;
 }
@@ -992,10 +1003,12 @@
             Layer::State& front;
             Layer::State& current;
             bool& recomputeVisibleRegions;
+            bool stickyTransformSet;
             Reject(Layer::State& front, Layer::State& current,
-                    bool& recomputeVisibleRegions)
+                    bool& recomputeVisibleRegions, bool stickySet)
                 : front(front), current(current),
-                  recomputeVisibleRegions(recomputeVisibleRegions) {
+                  recomputeVisibleRegions(recomputeVisibleRegions),
+                  stickyTransformSet(stickySet) {
             }
 
             virtual bool reject(const sp<GraphicBuffer>& buf,
@@ -1058,12 +1071,12 @@
                             front.requested.crop.getHeight());
                 }
 
-                if (!isFixedSize) {
+                if (!isFixedSize && !stickyTransformSet) {
                     if (front.active.w != bufWidth ||
                         front.active.h != bufHeight) {
                         // reject this buffer
-                        //ALOGD("rejecting buffer: bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
-                        //        bufWidth, bufHeight, front.active.w, front.active.h);
+                        ALOGE("rejecting buffer: bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}",
+                                bufWidth, bufHeight, front.active.w, front.active.h);
                         return true;
                     }
                 }
@@ -1092,8 +1105,8 @@
             }
         };
 
-
-        Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions);
+        Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
+                getProducerStickyTransform() != 0);
 
         status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r,
                 mFlinger->mPrimaryDispSync);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index ee9f8a0..2d8084d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -339,6 +339,9 @@
     void drawWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip,
             bool useIdentityTransform) const;
 
+    // Temporary - Used only for LEGACY camera mode.
+    uint32_t getProducerStickyTransform() const;
+
 
     // -----------------------------------------------------------------------