OSDN Git Service

am 3ee47188: Frame rate to 48 fps
authorDave Burke <daveburke@google.com>
Fri, 4 Nov 2011 20:55:30 +0000 (20:55 +0000)
committerAndroid Git Automerger <android-git-automerger@android.com>
Fri, 4 Nov 2011 20:55:30 +0000 (20:55 +0000)
* commit '3ee471889b09d90affddd00c409a515a64b6861b':
  Frame rate to 48 fps

19 files changed:
include/binder/CursorWindow.h
include/surfaceflinger/ISurfaceComposer.h
include/surfaceflinger/SurfaceComposerClient.h
include/utils/BlobCache.h
include/utils/Singleton.h
libs/binder/CursorWindow.cpp
libs/gui/ISurfaceComposer.cpp
libs/gui/SurfaceComposerClient.cpp
libs/gui/SurfaceTexture.cpp
libs/gui/tests/SurfaceTexture_test.cpp
libs/utils/Android.mk
libs/utils/BlobCache.cpp
libs/utils/tests/BlobCache_test.cpp
services/surfaceflinger/Layer.cpp
services/surfaceflinger/Layer.h
services/surfaceflinger/SurfaceFlinger.cpp
services/surfaceflinger/SurfaceFlinger.h
services/surfaceflinger/tests/Android.mk
services/surfaceflinger/tests/Transaction_test.cpp [new file with mode: 0644]

index 5d490ed..f0284de 100644 (file)
@@ -80,8 +80,7 @@ public:
 
     ~CursorWindow();
 
-    static status_t create(const String8& name, size_t size, bool localOnly,
-            CursorWindow** outCursorWindow);
+    static status_t create(const String8& name, size_t size, CursorWindow** outCursorWindow);
     static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow);
 
     status_t writeToParcel(Parcel* parcel);
index e7a33f1..5eb09c7 100644 (file)
@@ -84,7 +84,11 @@ public:
         eOrientationUnchanged   = 4,
         eOrientationSwapMask    = 0x01
     };
-    
+
+    enum {
+        eSynchronous            = 0x01,
+    };
+
     enum {
         eElectronBeamAnimationOn  = 0x01,
         eElectronBeamAnimationOff = 0x10
@@ -104,7 +108,7 @@ public:
 
     /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
     virtual void setTransactionState(const Vector<ComposerState>& state,
-            int orientation) = 0;
+            int orientation, uint32_t flags) = 0;
 
     /* signal that we're done booting.
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -143,8 +147,6 @@ public:
         GET_CBLK,
         SET_TRANSACTION_STATE,
         SET_ORIENTATION,
-        FREEZE_DISPLAY,
-        UNFREEZE_DISPLAY,
         CAPTURE_SCREEN,
         TURN_ELECTRON_BEAM_OFF,
         TURN_ELECTRON_BEAM_ON,
index 14e5b23..8226abe 100644 (file)
@@ -112,7 +112,7 @@ public:
     static void openGlobalTransaction();
         
     //! Close a composer transaction on all active SurfaceComposerClients.
-    static void closeGlobalTransaction();
+    static void closeGlobalTransaction(bool synchronous = false);
     
     //! Freeze the specified display but not transactions.
     static status_t freezeDisplay(DisplayID dpy, uint32_t flags = 0);
index dc45ff0..4f342a2 100644 (file)
 
 #include <stddef.h>
 
+#include <utils/Flattenable.h>
 #include <utils/RefBase.h>
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
 namespace android {
 
-// A BlobCache is an in-memory cache for binary key/value pairs. All the public
-// methods are thread-safe.
+// A BlobCache is an in-memory cache for binary key/value pairs.  A BlobCache
+// does NOT provide any thread-safety guarantees.
 //
-// The cache contents can be serialized to a file and reloaded in a subsequent
-// execution of the program. This serialization is non-portable and should only
-// be loaded by the device that generated it.
-class BlobCache : public RefBase {
+// The cache contents can be serialized to an in-memory buffer or mmap'd file
+// and then reloaded in a subsequent execution of the program.  This
+// serialization is non-portable and the data should only be used by the device
+// that generated it.
+class BlobCache : public RefBase, public Flattenable {
 public:
 
     // Create an empty blob cache. The blob cache will cache key/value pairs
@@ -58,14 +60,13 @@ public:
     void set(const void* key, size_t keySize, const void* value,
             size_t valueSize);
 
-    // The get function retrieves from the cache the binary value associated
-    // with a given binary key.  If the key is present in the cache then the
-    // length of the binary value associated with that key is returned.  If the
-    // value argument is non-NULL and the size of the cached value is less than
-    // valueSize bytes then the cached value is copied into the buffer pointed
-    // to by the value argument.  If the key is not present in the cache then 0
-    // is returned and the buffer pointed to by the value argument is not
-    // modified.
+    // get retrieves from the cache the binary value associated with a given
+    // binary key.  If the key is present in the cache then the length of the
+    // binary value associated with that key is returned.  If the value argument
+    // is non-NULL and the size of the cached value is less than valueSize bytes
+    // then the cached value is copied into the buffer pointed to by the value
+    // argument.  If the key is not present in the cache then 0 is returned and
+    // the buffer pointed to by the value argument is not modified.
     //
     // Note that when calling get multiple times with the same key, the later
     // calls may fail, returning 0, even if earlier calls succeeded.  The return
@@ -77,6 +78,37 @@ public:
     //   0 <= valueSize
     size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
 
+    // getFlattenedSize returns the number of bytes needed to store the entire
+    // serialized cache.
+    virtual size_t getFlattenedSize() const;
+
+    // getFdCount returns the number of file descriptors that will result from
+    // flattening the cache.  This will always return 0 so as to allow the
+    // flattened cache to be saved to disk and then later restored.
+    virtual size_t getFdCount() const;
+
+    // flatten serializes the current contents of the cache into the memory
+    // pointed to by 'buffer'.  The serialized cache contents can later be
+    // loaded into a BlobCache object using the unflatten method.  The contents
+    // of the BlobCache object will not be modified.
+    //
+    // Preconditions:
+    //   size >= this.getFlattenedSize()
+    //   count == 0
+    virtual status_t flatten(void* buffer, size_t size, int fds[],
+            size_t count) const;
+
+    // unflatten replaces the contents of the cache with the serialized cache
+    // contents in the memory pointed to by 'buffer'.  The previous contents of
+    // the BlobCache will be evicted from the cache.  If an error occurs while
+    // unflattening the serialized cache contents then the BlobCache will be
+    // left in an empty state.
+    //
+    // Preconditions:
+    //   count == 0
+    virtual status_t unflatten(void const* buffer, size_t size, int fds[],
+            size_t count);
+
 private:
     // Copying is disallowed.
     BlobCache(const BlobCache&);
@@ -144,6 +176,46 @@ private:
         sp<Blob> mValue;
     };
 
+    // A Header is the header for the entire BlobCache serialization format. No
+    // need to make this portable, so we simply write the struct out.
+    struct Header {
+        // mMagicNumber is the magic number that identifies the data as
+        // serialized BlobCache contents.  It must always contain 'Blb$'.
+        uint32_t mMagicNumber;
+
+        // mBlobCacheVersion is the serialization format version.
+        uint32_t mBlobCacheVersion;
+
+        // mDeviceVersion is the device-specific version of the cache.  This can
+        // be used to invalidate the cache.
+        uint32_t mDeviceVersion;
+
+        // mNumEntries is number of cache entries following the header in the
+        // data.
+        size_t mNumEntries;
+    };
+
+    // An EntryHeader is the header for a serialized cache entry.  No need to
+    // make this portable, so we simply write the struct out.  Each EntryHeader
+    // is followed imediately by the key data and then the value data.
+    //
+    // The beginning of each serialized EntryHeader is 4-byte aligned, so the
+    // number of bytes that a serialized cache entry will occupy is:
+    //
+    //   ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
+    //
+    struct EntryHeader {
+        // mKeySize is the size of the entry key in bytes.
+        size_t mKeySize;
+
+        // mValueSize is the size of the entry value in bytes.
+        size_t mValueSize;
+
+        // mData contains both the key and value data for the cache entry.  The
+        // key comes first followed immediately by the value.
+        uint8_t mData[];
+    };
+
     // mMaxKeySize is the maximum key size that will be cached. Calls to
     // BlobCache::set with a keySize parameter larger than mMaxKeySize will
     // simply not add the key/value pair to the cache.
@@ -166,17 +238,12 @@ private:
     size_t mTotalSize;
 
     // mRandState is the pseudo-random number generator state. It is passed to
-    // nrand48 to generate random numbers when needed. It must be protected by
-    // mMutex.
+    // nrand48 to generate random numbers when needed.
     unsigned short mRandState[3];
 
     // mCacheEntries stores all the cache entries that are resident in memory.
     // Cache entries are added to it by the 'set' method.
     SortedVector<CacheEntry> mCacheEntries;
-
-    // mMutex is used to synchronize access to all member variables.  It must be
-    // locked any time the member variables are written or read.
-    Mutex mMutex;
 };
 
 }
index e1ee8eb..a42ce21 100644 (file)
 #include <stdint.h>
 #include <sys/types.h>
 #include <utils/threads.h>
+#include <cutils/compiler.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
 
 template <typename TYPE>
-class Singleton
+class ANDROID_API Singleton
 {
 public:
     static TYPE& getInstance() {
index 60681c4..0733378 100644 (file)
@@ -40,11 +40,9 @@ CursorWindow::~CursorWindow() {
     ::close(mAshmemFd);
 }
 
-status_t CursorWindow::create(const String8& name, size_t size, bool localOnly,
-        CursorWindow** outCursorWindow) {
+status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) {
     String8 ashmemName("CursorWindow: ");
     ashmemName.append(name);
-    ashmemName.append(localOnly ? " (local)" : " (remote)");
 
     status_t result;
     int ashmemFd = ashmem_create_region(ashmemName.string(), size);
index eb90147..86bc62a 100644 (file)
@@ -79,7 +79,7 @@ public:
     }
 
     virtual void setTransactionState(const Vector<ComposerState>& state,
-            int orientation)
+            int orientation, uint32_t flags)
     {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -90,6 +90,7 @@ public:
             b->write(data);
         }
         data.writeInt32(orientation);
+        data.writeInt32(flags);
         remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
     }
 
@@ -204,7 +205,8 @@ status_t BnSurfaceComposer::onTransact(
                 state.add(s);
             }
             int orientation = data.readInt32();
-            setTransactionState(state, orientation);
+            uint32_t flags = data.readInt32();
+            setTransactionState(state, orientation, flags);
         } break;
         case BOOT_FINISHED: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
index 5f3d608..4ad6c22 100644 (file)
@@ -92,11 +92,14 @@ class Composer : public Singleton<Composer>
     mutable Mutex               mLock;
     SortedVector<ComposerState> mStates;
     int                         mOrientation;
+    uint32_t                    mForceSynchronous;
 
     Composer() : Singleton<Composer>(),
-        mOrientation(ISurfaceComposer::eOrientationUnchanged) { }
+        mOrientation(ISurfaceComposer::eOrientationUnchanged),
+        mForceSynchronous(0)
+    { }
 
-    void closeGlobalTransactionImpl();
+    void closeGlobalTransactionImpl(bool synchronous);
 
     layer_state_t* getLayerStateLocked(
             const sp<SurfaceComposerClient>& client, SurfaceID id);
@@ -123,8 +126,8 @@ public:
             uint32_t tint);
     status_t setOrientation(int orientation);
 
-    static void closeGlobalTransaction() {
-        Composer::getInstance().closeGlobalTransactionImpl();
+    static void closeGlobalTransaction(bool synchronous) {
+        Composer::getInstance().closeGlobalTransactionImpl(synchronous);
     }
 };
 
@@ -132,11 +135,12 @@ ANDROID_SINGLETON_STATIC_INSTANCE(Composer);
 
 // ---------------------------------------------------------------------------
 
-void Composer::closeGlobalTransactionImpl() {
+void Composer::closeGlobalTransactionImpl(bool synchronous) {
     sp<ISurfaceComposer> sm(getComposerService());
 
     Vector<ComposerState> transaction;
     int orientation;
+    uint32_t flags = 0;
 
     { // scope for the lock
         Mutex::Autolock _l(mLock);
@@ -145,9 +149,14 @@ void Composer::closeGlobalTransactionImpl() {
 
         orientation = mOrientation;
         mOrientation = ISurfaceComposer::eOrientationUnchanged;
+
+        if (synchronous || mForceSynchronous) {
+            flags |= ISurfaceComposer::eSynchronous;
+        }
+        mForceSynchronous = false;
     }
 
-   sm->setTransactionState(transaction, orientation);
+   sm->setTransactionState(transaction, orientation, flags);
 }
 
 layer_state_t* Composer::getLayerStateLocked(
@@ -188,6 +197,10 @@ status_t Composer::setSize(const sp<SurfaceComposerClient>& client,
     s->what |= ISurfaceComposer::eSizeChanged;
     s->w = w;
     s->h = h;
+
+    // Resizing a surface makes the transaction synchronous.
+    mForceSynchronous = true;
+
     return NO_ERROR;
 }
 
@@ -270,6 +283,10 @@ status_t Composer::setFreezeTint(const sp<SurfaceComposerClient>& client,
 status_t Composer::setOrientation(int orientation) {
     Mutex::Autolock _l(mLock);
     mOrientation = orientation;
+
+    // Changing the orientation makes the transaction synchronous.
+    mForceSynchronous = true;
+
     return NO_ERROR;
 }
 
@@ -375,8 +392,8 @@ void SurfaceComposerClient::openGlobalTransaction() {
     // Currently a no-op
 }
 
-void SurfaceComposerClient::closeGlobalTransaction() {
-    Composer::closeGlobalTransaction();
+void SurfaceComposerClient::closeGlobalTransaction(bool synchronous) {
+    Composer::closeGlobalTransaction(synchronous);
 }
 
 // ----------------------------------------------------------------------------
index c72a45b..6f84206 100644 (file)
@@ -116,7 +116,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
     // Choose a name using the PID and a process-unique ID.
     mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
 
-    ST_LOGV("SurfaceTexture::SurfaceTexture");
+    ST_LOGV("SurfaceTexture");
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
     mNextCrop.makeInvalid();
@@ -125,7 +125,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
 }
 
 SurfaceTexture::~SurfaceTexture() {
-    ST_LOGV("SurfaceTexture::~SurfaceTexture");
+    ST_LOGV("~SurfaceTexture");
     freeAllBuffersLocked();
 }
 
@@ -169,7 +169,7 @@ status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
 }
 
 status_t SurfaceTexture::setBufferCount(int bufferCount) {
-    ST_LOGV("SurfaceTexture::setBufferCount");
+    ST_LOGV("setBufferCount: count=%d", bufferCount);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -217,6 +217,7 @@ status_t SurfaceTexture::setBufferCount(int bufferCount) {
 
 status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
 {
+    ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
     if (!w || !h) {
         ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
                 w, h);
@@ -230,7 +231,7 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
 }
 
 status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
-    ST_LOGV("SurfaceTexture::requestBuffer");
+    ST_LOGV("requestBuffer: slot=%d", slot);
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
         ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
@@ -248,7 +249,7 @@ status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
 
 status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
         uint32_t format, uint32_t usage) {
-    ST_LOGV("SurfaceTexture::dequeueBuffer");
+    ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
 
     if ((w && !h) || (!w && h)) {
         ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
@@ -342,6 +343,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
         // clients are not allowed to dequeue more than one buffer
         // if they didn't set a buffer count.
         if (!mClientBufferCount && dequeuedCount) {
+            ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
+                    "setting the buffer count");
             return -EINVAL;
         }
 
@@ -375,6 +378,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
     }
 
     if (found == INVALID_BUFFER_SLOT) {
+        // This should not happen.
+        ST_LOGE("dequeueBuffer: no available buffer slots");
         return -EBUSY;
     }
 
@@ -427,10 +432,13 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
         }
         returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
     }
+    ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf,
+            mSlots[buf].mGraphicBuffer->handle, returnFlags);
     return returnFlags;
 }
 
 status_t SurfaceTexture::setSynchronousMode(bool enabled) {
+    ST_LOGV("setSynchronousMode: enabled=%d", enabled);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -462,7 +470,7 @@ status_t SurfaceTexture::setSynchronousMode(bool enabled) {
 
 status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
         uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
-    ST_LOGV("SurfaceTexture::queueBuffer");
+    ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
 
     sp<FrameAvailableListener> listener;
 
@@ -534,7 +542,7 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
 }
 
 void SurfaceTexture::cancelBuffer(int buf) {
-    ST_LOGV("SurfaceTexture::cancelBuffer");
+    ST_LOGV("cancelBuffer: slot=%d", buf);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -556,7 +564,9 @@ void SurfaceTexture::cancelBuffer(int buf) {
 }
 
 status_t SurfaceTexture::setCrop(const Rect& crop) {
-    ST_LOGV("SurfaceTexture::setCrop");
+    ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
+            crop.bottom);
+
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
         ST_LOGE("setCrop: SurfaceTexture has been abandoned!");
@@ -567,7 +577,7 @@ status_t SurfaceTexture::setCrop(const Rect& crop) {
 }
 
 status_t SurfaceTexture::setTransform(uint32_t transform) {
-    ST_LOGV("SurfaceTexture::setTransform");
+    ST_LOGV("setTransform: xform=%#x", transform);
     Mutex::Autolock lock(mMutex);
     if (mAbandoned) {
         ST_LOGE("setTransform: SurfaceTexture has been abandoned!");
@@ -579,7 +589,7 @@ status_t SurfaceTexture::setTransform(uint32_t transform) {
 
 status_t SurfaceTexture::connect(int api,
         uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
-    ST_LOGV("SurfaceTexture::connect(this=%p, %d)", this, api);
+    ST_LOGV("connect: api=%d", api);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -612,7 +622,7 @@ status_t SurfaceTexture::connect(int api,
 }
 
 status_t SurfaceTexture::disconnect(int api) {
-    ST_LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api);
+    ST_LOGV("disconnect: api=%d", api);
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -640,6 +650,7 @@ status_t SurfaceTexture::disconnect(int api) {
             }
             break;
         default:
+            ST_LOGE("disconnect: unknown API %d", api);
             err = -EINVAL;
             break;
     }
@@ -647,13 +658,14 @@ status_t SurfaceTexture::disconnect(int api) {
 }
 
 status_t SurfaceTexture::setScalingMode(int mode) {
-    ST_LOGV("SurfaceTexture::setScalingMode(%d)", mode);
+    ST_LOGV("setScalingMode: mode=%d", mode);
 
     switch (mode) {
         case NATIVE_WINDOW_SCALING_MODE_FREEZE:
         case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
             break;
         default:
+            ST_LOGE("unknown scaling mode: %d", mode);
             return BAD_VALUE;
     }
 
@@ -663,7 +675,7 @@ status_t SurfaceTexture::setScalingMode(int mode) {
 }
 
 status_t SurfaceTexture::updateTexImage() {
-    ST_LOGV("SurfaceTexture::updateTexImage");
+    ST_LOGV("updateTexImage");
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
@@ -713,6 +725,10 @@ status_t SurfaceTexture::updateTexImage() {
             return -EINVAL;
         }
 
+        ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
+                mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf,
+                mSlots[buf].mGraphicBuffer->handle);
+
         if (mCurrentTexture != INVALID_BUFFER_SLOT) {
             // The current buffer becomes FREE if it was still in the queued
             // state. If it has already been given to the client
@@ -771,7 +787,7 @@ void SurfaceTexture::getTransformMatrix(float mtx[16]) {
 }
 
 void SurfaceTexture::computeCurrentTransformMatrix() {
-    ST_LOGV("SurfaceTexture::computeCurrentTransformMatrix");
+    ST_LOGV("computeCurrentTransformMatrix");
 
     float xform[16];
     for (int i = 0; i < 16; i++) {
@@ -862,14 +878,14 @@ void SurfaceTexture::computeCurrentTransformMatrix() {
 }
 
 nsecs_t SurfaceTexture::getTimestamp() {
-    ST_LOGV("SurfaceTexture::getTimestamp");
+    ST_LOGV("getTimestamp");
     Mutex::Autolock lock(mMutex);
     return mCurrentTimestamp;
 }
 
 void SurfaceTexture::setFrameAvailableListener(
         const sp<FrameAvailableListener>& listener) {
-    ST_LOGV("SurfaceTexture::setFrameAvailableListener");
+    ST_LOGV("setFrameAvailableListener");
     Mutex::Autolock lock(mMutex);
     mFrameAvailableListener = listener;
 }
index b8bc454..5daafd5 100644 (file)
@@ -334,7 +334,7 @@ protected:
 
 class SurfaceTextureGLTest : public GLTest {
 protected:
-    static const GLint TEX_ID = 123;
+    enum { TEX_ID = 123 };
 
     virtual void SetUp() {
         GLTest::SetUp();
@@ -1438,4 +1438,86 @@ TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalled
     }
 }
 
+TEST_F(SurfaceTextureGLTest, EglDestroySurfaceUnrefsBuffers) {
+    EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
+            mANW.get(), NULL);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);
+
+    sp<GraphicBuffer> buffers[3];
+
+    for (int i = 0; i < 3; i++) {
+        // Produce a frame
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
+                mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        glClear(GL_COLOR_BUFFER_BIT);
+        eglSwapBuffers(mEglDisplay, stcEglSurface);
+
+        // Consume a frame
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+                mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        mST->updateTexImage();
+        buffers[i] = mST->getCurrentBuffer();
+    }
+
+    // Destroy the GL texture object to release its ref on buffers[2].
+    GLuint texID = TEX_ID;
+    glDeleteTextures(1, &texID);
+
+    // Destroy the EGLSurface
+    EXPECT_TRUE(eglDestroySurface(mEglDisplay, stcEglSurface));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    // Release the ref that the SurfaceTexture has on buffers[2].
+    mST->abandon();
+
+    EXPECT_EQ(1, buffers[0]->getStrongCount());
+    EXPECT_EQ(1, buffers[1]->getStrongCount());
+    EXPECT_EQ(1, buffers[2]->getStrongCount());
+}
+
+TEST_F(SurfaceTextureGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) {
+    EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
+            mANW.get(), NULL);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);
+
+    sp<GraphicBuffer> buffers[3];
+
+    for (int i = 0; i < 3; i++) {
+        // Produce a frame
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
+                mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        glClear(GL_COLOR_BUFFER_BIT);
+        EXPECT_TRUE(eglSwapBuffers(mEglDisplay, stcEglSurface));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        // Consume a frame
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+                mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+        buffers[i] = mST->getCurrentBuffer();
+    }
+
+    // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has
+    // on buffers[2].
+    mST->abandon();
+
+    // Destroy the GL texture object to release its ref on buffers[2].
+    GLuint texID = TEX_ID;
+    glDeleteTextures(1, &texID);
+
+    // Destroy the EGLSurface.
+    EXPECT_TRUE(eglDestroySurface(mEglDisplay, stcEglSurface));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    EXPECT_EQ(1, buffers[0]->getStrongCount());
+    EXPECT_EQ(1, buffers[1]->getStrongCount());
+    EXPECT_EQ(1, buffers[2]->getStrongCount());
+}
+
 } // namespace android
index 638f72f..831d9e3 100644 (file)
@@ -71,6 +71,10 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1
 endif
 endif
 
+ifeq ($(TARGET_OS),linux)
+LOCAL_LDLIBS += -lrt -ldl
+endif
+
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 
index 590576a..d38aae9 100644 (file)
 #include <string.h>
 
 #include <utils/BlobCache.h>
+#include <utils/Errors.h>
 #include <utils/Log.h>
 
 namespace android {
 
+// BlobCache::Header::mMagicNumber value
+static const uint32_t blobCacheMagic = '_Bb$';
+
+// BlobCache::Header::mBlobCacheVersion value
+static const uint32_t blobCacheVersion = 1;
+
+// BlobCache::Header::mDeviceVersion value
+static const uint32_t blobCacheDeviceVersion = 1;
+
 BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
         mMaxKeySize(maxKeySize),
         mMaxValueSize(maxValueSize),
@@ -67,12 +77,10 @@ void BlobCache::set(const void* key, size_t keySize, const void* value,
         return;
     }
 
-    Mutex::Autolock lock(mMutex);
     sp<Blob> dummyKey(new Blob(key, keySize, false));
     CacheEntry dummyEntry(dummyKey, NULL);
 
     while (true) {
-
         ssize_t index = mCacheEntries.indexOf(dummyEntry);
         if (index < 0) {
             // Create a new cache entry.
@@ -129,7 +137,6 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value,
                 keySize, mMaxKeySize);
         return 0;
     }
-    Mutex::Autolock lock(mMutex);
     sp<Blob> dummyKey(new Blob(key, keySize, false));
     CacheEntry dummyEntry(dummyKey, NULL);
     ssize_t index = mCacheEntries.indexOf(dummyEntry);
@@ -152,6 +159,133 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value,
     return valueBlobSize;
 }
 
+static inline size_t align4(size_t size) {
+    return (size + 3) & ~3;
+}
+
+size_t BlobCache::getFlattenedSize() const {
+    size_t size = sizeof(Header);
+    for (size_t i = 0; i < mCacheEntries.size(); i++) {
+        const CacheEntry& e(mCacheEntries[i]);
+        sp<Blob> keyBlob = e.getKey();
+        sp<Blob> valueBlob = e.getValue();
+        size = align4(size);
+        size += sizeof(EntryHeader) + keyBlob->getSize() +
+                valueBlob->getSize();
+    }
+    return size;
+}
+
+size_t BlobCache::getFdCount() const {
+    return 0;
+}
+
+status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count)
+        const {
+    if (count != 0) {
+        LOGE("flatten: nonzero fd count: %d", count);
+        return BAD_VALUE;
+    }
+
+    // Write the cache header
+    if (size < sizeof(Header)) {
+        LOGE("flatten: not enough room for cache header");
+        return BAD_VALUE;
+    }
+    Header* header = reinterpret_cast<Header*>(buffer);
+    header->mMagicNumber = blobCacheMagic;
+    header->mBlobCacheVersion = blobCacheVersion;
+    header->mDeviceVersion = blobCacheDeviceVersion;
+    header->mNumEntries = mCacheEntries.size();
+
+    // Write cache entries
+    uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
+    off_t byteOffset = align4(sizeof(Header));
+    for (size_t i = 0; i < mCacheEntries.size(); i++) {
+        const CacheEntry& e(mCacheEntries[i]);
+        sp<Blob> keyBlob = e.getKey();
+        sp<Blob> valueBlob = e.getValue();
+        size_t keySize = keyBlob->getSize();
+        size_t valueSize = valueBlob->getSize();
+
+        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+        if (byteOffset + entrySize > size) {
+            LOGE("flatten: not enough room for cache entries");
+            return BAD_VALUE;
+        }
+
+        EntryHeader* eheader = reinterpret_cast<EntryHeader*>(
+            &byteBuffer[byteOffset]);
+        eheader->mKeySize = keySize;
+        eheader->mValueSize = valueSize;
+
+        memcpy(eheader->mData, keyBlob->getData(), keySize);
+        memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
+
+        byteOffset += align4(entrySize);
+    }
+
+    return OK;
+}
+
+status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[],
+        size_t count) {
+    // All errors should result in the BlobCache being in an empty state.
+    mCacheEntries.clear();
+
+    if (count != 0) {
+        LOGE("unflatten: nonzero fd count: %d", count);
+        return BAD_VALUE;
+    }
+
+    // Read the cache header
+    if (size < sizeof(Header)) {
+        LOGE("unflatten: not enough room for cache header");
+        return BAD_VALUE;
+    }
+    const Header* header = reinterpret_cast<const Header*>(buffer);
+    if (header->mMagicNumber != blobCacheMagic) {
+        LOGE("unflatten: bad magic number: %d", header->mMagicNumber);
+        return BAD_VALUE;
+    }
+    if (header->mBlobCacheVersion != blobCacheVersion ||
+            header->mDeviceVersion != blobCacheDeviceVersion) {
+        // We treat version mismatches as an empty cache.
+        return OK;
+    }
+
+    // Read cache entries
+    const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
+    off_t byteOffset = align4(sizeof(Header));
+    size_t numEntries = header->mNumEntries;
+    for (size_t i = 0; i < numEntries; i++) {
+        if (byteOffset + sizeof(EntryHeader) > size) {
+            mCacheEntries.clear();
+            LOGE("unflatten: not enough room for cache entry headers");
+            return BAD_VALUE;
+        }
+
+        const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
+                &byteBuffer[byteOffset]);
+        size_t keySize = eheader->mKeySize;
+        size_t valueSize = eheader->mValueSize;
+        size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+
+        if (byteOffset + entrySize > size) {
+            mCacheEntries.clear();
+            LOGE("unflatten: not enough room for cache entry headers");
+            return BAD_VALUE;
+        }
+
+        const uint8_t* data = eheader->mData;
+        set(data, keySize, data + keySize, valueSize);
+
+        byteOffset += align4(entrySize);
+    }
+
+    return OK;
+}
+
 long int BlobCache::blob_random() {
 #ifdef _WIN32
     return rand();
@@ -179,7 +313,7 @@ BlobCache::Blob::Blob(const void* data, size_t size, bool copyData):
         mData(copyData ? malloc(size) : data),
         mSize(size),
         mOwnsData(copyData) {
-    if (copyData) {
+    if (data != NULL && copyData) {
         memcpy(const_cast<void*>(mData), data, size);
     }
 }
index 653ea5e..b64cc39 100644 (file)
  ** limitations under the License.
  */
 
+#include <fcntl.h>
+#include <stdio.h>
+
 #include <gtest/gtest.h>
 
 #include <utils/BlobCache.h>
+#include <utils/Errors.h>
 
 namespace android {
 
@@ -254,4 +258,164 @@ TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
     ASSERT_EQ(maxEntries/2 + 1, numCached);
 }
 
+class BlobCacheFlattenTest : public BlobCacheTest {
+protected:
+    virtual void SetUp() {
+        BlobCacheTest::SetUp();
+        mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
+    }
+
+    virtual void TearDown() {
+        mBC2.clear();
+        BlobCacheTest::TearDown();
+    }
+
+    void roundTrip() {
+        size_t size = mBC->getFlattenedSize();
+        uint8_t* flat = new uint8_t[size];
+        ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+        ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+        delete[] flat;
+    }
+
+    sp<BlobCache> mBC2;
+};
+
+TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+    roundTrip();
+    ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
+    ASSERT_EQ('e', buf[0]);
+    ASSERT_EQ('f', buf[1]);
+    ASSERT_EQ('g', buf[2]);
+    ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    roundTrip();
+
+    // Verify the deserialized cache
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        uint8_t v = 0xee;
+        ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
+        ASSERT_EQ(k, v);
+    }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // Verify the cache that we just serialized
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        uint8_t v = 0xee;
+        ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
+        ASSERT_EQ(k, v);
+    }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
+    // Fill up the entire cache with 1 char key/value pairs.
+    const int maxEntries = MAX_TOTAL_SIZE / 2;
+    for (int i = 0; i < maxEntries; i++) {
+        uint8_t k = i;
+        mBC->set(&k, 1, &k, 1);
+    }
+
+    size_t size = mBC->getFlattenedSize() - 1;
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size, NULL, 0));
+    delete[] flat;
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    flat[1] = ~flat[1];
+
+    // Bad magic should cause an error.
+    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // The error should cause the unflatten to result in an empty cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    flat[5] = ~flat[5];
+
+    // Version mismatches shouldn't cause errors, but should not use the
+    // serialized entries
+    ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // The version mismatch should cause the unflatten to result in an empty
+    // cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+    flat[10] = ~flat[10];
+
+    // Version mismatches shouldn't cause errors, but should not use the
+    // serialized entries
+    ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+    delete[] flat;
+
+    // The version mismatch should cause the unflatten to result in an empty
+    // cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
+    char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+    mBC->set("abcd", 4, "efgh", 4);
+
+    size_t size = mBC->getFlattenedSize();
+    uint8_t* flat = new uint8_t[size];
+    ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+
+    // A buffer truncation shouldt cause an error
+    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1, NULL, 0));
+    delete[] flat;
+
+    // The error should cause the unflatten to result in an empty cache
+    ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
 } // namespace android
index 50b8604..d3b0dbf 100644 (file)
@@ -360,18 +360,6 @@ uint32_t Layer::doTransaction(uint32_t flags)
                 mCurrentScalingMode);
 
         if (!isFixedSize()) {
-            // we're being resized and there is a freeze display request,
-            // acquire a freeze lock, so that the screen stays put
-            // until we've redrawn at the new size; this is to avoid
-            // glitches upon orientation changes.
-            if (mFlinger->hasFreezeRequest()) {
-                // if the surface is hidden, don't try to acquire the
-                // freeze lock, since hidden surfaces may never redraw
-                if (!(front.flags & ISurfaceComposer::eLayerHidden)) {
-                    mFreezeLock = mFlinger->getFreezeLock();
-                }
-            }
-
             // this will make sure LayerBase::doTransaction doesn't update
             // the drawing state's size
             Layer::State& editDraw(mDrawingState);
@@ -385,14 +373,6 @@ uint32_t Layer::doTransaction(uint32_t flags)
                 temp.requested_h);
     }
 
-    if (temp.sequence != front.sequence) {
-        if (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha == 0) {
-            // this surface is now hidden, so it shouldn't hold a freeze lock
-            // (it may never redraw, which is fine if it is hidden)
-            mFreezeLock.clear();
-        }
-    }
-        
     return LayerBase::doTransaction(flags);
 }
 
@@ -466,7 +446,7 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions)
         glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
         glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
-        // update the layer size and release freeze-lock
+        // update the layer size if needed
         const Layer::State& front(drawingState());
 
         // FIXME: mPostedDirtyRegion = dirty & bounds
@@ -503,9 +483,6 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions)
 
                 // recompute visible region
                 recomputeVisibleRegions = true;
-
-                // we now have the correct size, unfreeze the screen
-                mFreezeLock.clear();
             }
 
             LOGD_IF(DEBUG_RESIZE,
@@ -538,11 +515,6 @@ void Layer::unlockPageFlip(
         dirtyRegion.andSelf(visibleRegionScreen);
         outDirtyRegion.orSelf(dirtyRegion);
     }
-    if (visibleRegionScreen.isEmpty()) {
-        // an invisible layer should not hold a freeze-lock
-        // (because it may never be updated and therefore never release it)
-        mFreezeLock.clear();
-    }
 }
 
 void Layer::dump(String8& result, char* buffer, size_t SIZE) const
@@ -560,9 +532,9 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const
     snprintf(buffer, SIZE,
             "      "
             "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
-            " freezeLock=%p, transform-hint=0x%02x, queued-frames=%d\n",
+            " transform-hint=0x%02x, queued-frames=%d\n",
             mFormat, w0, h0, s0,f0,
-            getFreezeLock().get(), getTransformHint(), mQueuedFrames);
+            getTransformHint(), mQueuedFrames);
 
     result.append(buffer);
 
index 82e3521..2b9471b 100644 (file)
@@ -39,7 +39,6 @@ namespace android {
 
 // ---------------------------------------------------------------------------
 
-class FreezeLock;
 class Client;
 class GLExtensions;
 
@@ -80,7 +79,6 @@ public:
     virtual wp<IBinder> getSurfaceTextureBinder() const;
 
     // only for debugging
-    inline const sp<FreezeLock>&  getFreezeLock() const { return mFreezeLock; }
     inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; }
 
 protected:
@@ -124,9 +122,6 @@ private:
     bool mProtectedByApp; // application requires protected path to external sink
     Region mPostedDirtyRegion;
 
-    // page-flip thread and transaction thread (currently main thread)
-    sp<FreezeLock>  mFreezeLock;
-
     // binder thread, transaction thread
     mutable Mutex mLock;
 };
index ba8f630..a6d4147 100644 (file)
@@ -80,15 +80,12 @@ const String16 sDump("android.permission.DUMP");
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(), Thread(false),
         mTransactionFlags(0),
-        mResizeTransationPending(false),
+        mTransationPending(false),
         mLayersRemoved(false),
         mBootTime(systemTime()),
         mVisibleRegionsDirty(false),
         mHwWorkListDirty(false),
-        mFreezeDisplay(false),
         mElectronBeamAnimationMode(0),
-        mFreezeCount(0),
-        mFreezeDisplayTime(0),
         mDebugRegion(0),
         mDebugBackground(0),
         mDebugDDMS(0),
@@ -191,11 +188,6 @@ void SurfaceFlinger::binderDied(const wp<IBinder>& who)
 {
     // the window manager died on us. prepare its eulogy.
 
-    // unfreeze the screen in case it was... frozen
-    mFreezeDisplayTime = 0;
-    mFreezeCount = 0;
-    mFreezeDisplay = false;
-
     // reset screen orientation
     setOrientation(0, eOrientationDefault, 0);
 
@@ -323,33 +315,7 @@ void SurfaceFlinger::waitForEvent()
 {
     while (true) {
         nsecs_t timeout = -1;
-        const nsecs_t freezeDisplayTimeout = ms2ns(5000);
-        if (UNLIKELY(isFrozen())) {
-            // wait 5 seconds
-            const nsecs_t now = systemTime();
-            if (mFreezeDisplayTime == 0) {
-                mFreezeDisplayTime = now;
-            }
-            nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime);
-            timeout = waitTime>0 ? waitTime : 0;
-        }
-
         sp<MessageBase> msg = mEventQueue.waitMessage(timeout);
-
-        // see if we timed out
-        if (isFrozen()) {
-            const nsecs_t now = systemTime();
-            nsecs_t frozenTime = (now - mFreezeDisplayTime);
-            if (frozenTime >= freezeDisplayTimeout) {
-                // we timed out and are still frozen
-                LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d",
-                        mFreezeDisplay, mFreezeCount);
-                mFreezeDisplayTime = 0;
-                mFreezeCount = 0;
-                mFreezeDisplay = false;
-            }
-        }
-
         if (msg != 0) {
             switch (msg->what) {
                 case MessageQueue::INVALIDATE:
@@ -589,13 +555,6 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
             mDirtyRegion.set(hw.bounds());
         }
 
-        if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) {
-            // freezing or unfreezing the display -> trigger animation if needed
-            mFreezeDisplay = mCurrentState.freezeDisplay;
-            if (mFreezeDisplay)
-                 mFreezeDisplayTime = 0;
-        }
-
         if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) {
             // layers have been added
             mVisibleRegionsDirty = true;
@@ -621,11 +580,6 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
     commitTransaction();
 }
 
-sp<FreezeLock> SurfaceFlinger::getFreezeLock() const
-{
-    return new FreezeLock(const_cast<SurfaceFlinger *>(this));
-}
-
 void SurfaceFlinger::computeVisibleRegions(
     const LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion)
 {
@@ -755,7 +709,7 @@ void SurfaceFlinger::computeVisibleRegions(
 void SurfaceFlinger::commitTransaction()
 {
     mDrawingState = mCurrentState;
-    mResizeTransationPending = false;
+    mTransationPending = false;
     mTransactionCV.broadcast();
 }
 
@@ -1243,15 +1197,14 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags)
 
 
 void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state,
-        int orientation) {
+        int orientation, uint32_t flags) {
     Mutex::Autolock _l(mStateLock);
 
-    uint32_t flags = 0;
+    uint32_t transactionFlags = 0;
     if (mCurrentState.orientation != orientation) {
         if (uint32_t(orientation)<=eOrientation270 || orientation==42) {
             mCurrentState.orientation = orientation;
-            flags |= eTransactionNeeded;
-            mResizeTransationPending = true;
+            transactionFlags |= eTransactionNeeded;
         } else if (orientation != eOrientationUnchanged) {
             LOGW("setTransactionState: ignoring unrecognized orientation: %d",
                     orientation);
@@ -1262,56 +1215,29 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state,
     for (size_t i=0 ; i<count ; i++) {
         const ComposerState& s(state[i]);
         sp<Client> client( static_cast<Client *>(s.client.get()) );
-        flags |= setClientStateLocked(client, s.state);
+        transactionFlags |= setClientStateLocked(client, s.state);
     }
-    if (flags) {
-        setTransactionFlags(flags);
+    if (transactionFlags) {
+        setTransactionFlags(transactionFlags);
     }
 
-    signalEvent();
-
-    // if there is a transaction with a resize, wait for it to
-    // take effect before returning.
-    while (mResizeTransationPending) {
+    // if this is a synchronous transaction, wait for it to take effect before
+    // returning.
+    if (flags & eSynchronous) {
+        mTransationPending = true;
+    }
+    while (mTransationPending) {
         status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
         if (CC_UNLIKELY(err != NO_ERROR)) {
             // just in case something goes wrong in SF, return to the
             // called after a few seconds.
             LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!");
-            mResizeTransationPending = false;
+            mTransationPending = false;
             break;
         }
     }
 }
 
-status_t SurfaceFlinger::freezeDisplay(DisplayID dpy, uint32_t flags)
-{
-    if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
-        return BAD_VALUE;
-
-    Mutex::Autolock _l(mStateLock);
-    mCurrentState.freezeDisplay = 1;
-    setTransactionFlags(eTransactionNeeded);
-
-    // flags is intended to communicate some sort of animation behavior
-    // (for instance fading)
-    return NO_ERROR;
-}
-
-status_t SurfaceFlinger::unfreezeDisplay(DisplayID dpy, uint32_t flags)
-{
-    if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
-        return BAD_VALUE;
-
-    Mutex::Autolock _l(mStateLock);
-    mCurrentState.freezeDisplay = 0;
-    setTransactionFlags(eTransactionNeeded);
-
-    // flags is intended to communicate some sort of animation behavior
-    // (for instance fading)
-    return NO_ERROR;
-}
-
 int SurfaceFlinger::setOrientation(DisplayID dpy,
         int orientation, uint32_t flags)
 {
@@ -1512,7 +1438,6 @@ uint32_t SurfaceFlinger::setClientStateLocked(
         if (what & eSizeChanged) {
             if (layer->setSize(s.w, s.h)) {
                 flags |= eTraversalNeeded;
-                mResizeTransationPending = true;
             }
         }
         if (what & eAlphaChanged) {
@@ -1632,8 +1557,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
         mWormholeRegion.dump(result, "WormholeRegion");
         const DisplayHardware& hw(graphicPlane(0).displayHardware());
         snprintf(buffer, SIZE,
-                "  display frozen: %s, freezeCount=%d, orientation=%d, canDraw=%d\n",
-                mFreezeDisplay?"yes":"no", mFreezeCount,
+                "  orientation=%d, canDraw=%d\n",
                 mCurrentState.orientation, hw.canDraw());
         result.append(buffer);
         snprintf(buffer, SIZE,
@@ -1693,8 +1617,6 @@ status_t SurfaceFlinger::onTransact(
         case CREATE_CONNECTION:
         case SET_TRANSACTION_STATE:
         case SET_ORIENTATION:
-        case FREEZE_DISPLAY:
-        case UNFREEZE_DISPLAY:
         case BOOT_FINISHED:
         case TURN_ELECTRON_BEAM_OFF:
         case TURN_ELECTRON_BEAM_ON:
@@ -1766,10 +1688,6 @@ status_t SurfaceFlinger::onTransact(
                 GraphicLog::getInstance().setEnabled(enabled);
                 return NO_ERROR;
             }
-            case 1007: // set mFreezeCount
-                mFreezeCount = data.readInt32();
-                mFreezeDisplayTime = 0;
-                return NO_ERROR;
             case 1008:  // toggle use of hw composer
                 n = data.readInt32();
                 mDebugDisableHWC = n ? 1 : 0;
@@ -1866,8 +1784,10 @@ status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy,
     // redraw the screen entirely...
     glDisable(GL_TEXTURE_EXTERNAL_OES);
     glDisable(GL_TEXTURE_2D);
+    glDisable(GL_SCISSOR_TEST);
     glClearColor(0,0,0,1);
     glClear(GL_COLOR_BUFFER_BIT);
+    glEnable(GL_SCISSOR_TEST);
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
index 3284fdb..ea5bfa7 100644 (file)
@@ -46,7 +46,6 @@ namespace android {
 
 class Client;
 class DisplayHardware;
-class FreezeLock;
 class Layer;
 class LayerDim;
 class LayerScreenshot;
@@ -169,9 +168,7 @@ public:
     virtual sp<IMemoryHeap>             getCblk() const;
     virtual void                        bootFinished();
     virtual void                        setTransactionState(const Vector<ComposerState>& state,
-                                                            int orientation);
-    virtual status_t                    freezeDisplay(DisplayID dpy, uint32_t flags);
-    virtual status_t                    unfreezeDisplay(DisplayID dpy, uint32_t flags);
+                                                            int orientation, uint32_t flags);
     virtual int                         setOrientation(DisplayID dpy, int orientation, uint32_t flags);
     virtual bool                        authenticateSurfaceTexture(const sp<ISurfaceTexture>& surface) const;
 
@@ -269,12 +266,10 @@ private:
     struct State {
         State() {
             orientation = ISurfaceComposer::eOrientationDefault;
-            freezeDisplay = 0;
         }
         LayerVector     layersSortedByZ;
         uint8_t         orientation;
         uint8_t         orientationFlags;
-        uint8_t         freezeDisplay;
     };
 
     virtual bool        threadLoop();
@@ -336,20 +331,6 @@ private:
             status_t renderScreenToTextureLocked(DisplayID dpy,
                     GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
 
-            friend class FreezeLock;
-            sp<FreezeLock> getFreezeLock() const;
-            inline void incFreezeCount() {
-                if (mFreezeCount == 0)
-                    mFreezeDisplayTime = 0;
-                mFreezeCount++;
-            }
-            inline void decFreezeCount() { if (mFreezeCount > 0) mFreezeCount--; }
-            inline bool hasFreezeRequest() const { return mFreezeDisplay; }
-            inline bool isFrozen() const { 
-                return (mFreezeDisplay || mFreezeCount>0) && mBootFinished;
-            }
-
-            
             void        debugFlashRegions();
             void        debugShowFPS() const;
             void        drawWormhole() const;
@@ -363,7 +344,7 @@ private:
     volatile    int32_t                 mTransactionFlags;
                 Condition               mTransactionCV;
                 SortedVector< sp<LayerBase> > mLayerPurgatory;
-                bool                    mResizeTransationPending;
+                bool                    mTransationPending;
 
                 // protected by mStateLock (but we could use another lock)
                 GraphicPlane                mGraphicPlanes[1];
@@ -390,10 +371,7 @@ private:
                 Region                      mWormholeRegion;
                 bool                        mVisibleRegionsDirty;
                 bool                        mHwWorkListDirty;
-                bool                        mFreezeDisplay;
                 int32_t                     mElectronBeamAnimationMode;
-                int32_t                     mFreezeCount;
-                nsecs_t                     mFreezeDisplayTime;
                 Vector< sp<LayerBase> >     mVisibleLayersSortedByZ;
 
 
@@ -429,20 +407,6 @@ private:
 };
 
 // ---------------------------------------------------------------------------
-
-class FreezeLock : public LightRefBase<FreezeLock> {
-    SurfaceFlinger* mFlinger;
-public:
-    FreezeLock(SurfaceFlinger* flinger)
-        : mFlinger(flinger) {
-        mFlinger->incFreezeCount();
-    }
-    ~FreezeLock() {
-        mFlinger->decFreezeCount();
-    }
-};
-
-// ---------------------------------------------------------------------------
 }; // namespace android
 
 #endif // ANDROID_SURFACE_FLINGER_H
index 5053e7d..b655648 100644 (file)
@@ -1 +1,40 @@
-include $(call all-subdir-makefiles)
+# Build the unit tests,
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := SurfaceFlinger_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+    Transaction_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+       libEGL \
+       libGLESv2 \
+       libandroid \
+       libbinder \
+       libcutils \
+       libgui \
+       libstlport \
+       libui \
+       libutils \
+
+LOCAL_C_INCLUDES := \
+    bionic \
+    bionic/libstdc++/include \
+    external/gtest/include \
+    external/stlport/stlport \
+
+# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+# to integrate with auto-test framework.
+include $(BUILD_NATIVE_TEST)
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
new file mode 100644 (file)
index 0000000..afafd8a
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/IMemory.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+#include <utils/String8.h>
+
+namespace android {
+
+// Fill an RGBA_8888 formatted surface with a single color.
+static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc,
+        uint8_t r, uint8_t g, uint8_t b) {
+    Surface::SurfaceInfo info;
+    sp<Surface> s = sc->getSurface();
+    ASSERT_TRUE(s != NULL);
+    ASSERT_EQ(NO_ERROR, s->lock(&info));
+    uint8_t* img = reinterpret_cast<uint8_t*>(info.bits);
+    for (uint32_t y = 0; y < info.h; y++) {
+        for (uint32_t x = 0; x < info.w; x++) {
+            uint8_t* pixel = img + (4 * (y*info.s + x));
+            pixel[0] = r;
+            pixel[1] = g;
+            pixel[2] = b;
+            pixel[3] = 255;
+        }
+    }
+    ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+}
+
+// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
+// individual pixel values for testing purposes.
+class ScreenCapture : public RefBase {
+public:
+    static void captureScreen(sp<ScreenCapture>* sc) {
+        sp<IMemoryHeap> heap;
+        uint32_t w=0, h=0;
+        PixelFormat fmt=0;
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        ASSERT_EQ(NO_ERROR, sf->captureScreen(0, &heap, &w, &h, &fmt, 0, 0,
+                0, INT_MAX));
+        ASSERT_TRUE(heap != NULL);
+        ASSERT_EQ(PIXEL_FORMAT_RGBA_8888, fmt);
+        *sc = new ScreenCapture(w, h, heap);
+    }
+
+    void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
+        const uint8_t* img = reinterpret_cast<const uint8_t*>(mHeap->base());
+        const uint8_t* pixel = img + (4 * (y*mWidth + x));
+        if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
+            String8 err(String8::format("pixel @ (%3d, %3d): "
+                    "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
+                    x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
+            EXPECT_EQ(String8(), err);
+        }
+    }
+
+private:
+    ScreenCapture(uint32_t w, uint32_t h, const sp<IMemoryHeap>& heap) :
+        mWidth(w),
+        mHeight(h),
+        mHeap(heap)
+    {}
+
+    const uint32_t mWidth;
+    const uint32_t mHeight;
+    sp<IMemoryHeap> mHeap;
+};
+
+class LayerUpdateTest : public ::testing::Test {
+protected:
+    virtual void SetUp() {
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+        ssize_t displayWidth = mComposerClient->getDisplayWidth(0);
+        ssize_t displayHeight = mComposerClient->getDisplayHeight(0);
+
+        // Background surface
+        mBGSurfaceControl = mComposerClient->createSurface(
+                String8("BG Test Surface"), 0, displayWidth, displayHeight,
+                PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(mBGSurfaceControl != NULL);
+        ASSERT_TRUE(mBGSurfaceControl->isValid());
+        fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
+
+        // Foreground surface
+        mFGSurfaceControl = mComposerClient->createSurface(
+                String8("FG Test Surface"), 0, 64, 64, PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(mFGSurfaceControl != NULL);
+        ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+        fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+
+        // Synchronization surface
+        mSyncSurfaceControl = mComposerClient->createSurface(
+                String8("Sync Test Surface"), 0, 1, 1, PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(mSyncSurfaceControl != NULL);
+        ASSERT_TRUE(mSyncSurfaceControl->isValid());
+
+        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+
+        SurfaceComposerClient::openGlobalTransaction();
+
+        ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT_MAX-2));
+        ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
+
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX-1));
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64));
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
+
+        ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setLayer(INT_MAX-1));
+        ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->setPosition(displayWidth-2,
+                displayHeight-2));
+        ASSERT_EQ(NO_ERROR, mSyncSurfaceControl->show());
+
+        SurfaceComposerClient::closeGlobalTransaction(true);
+    }
+
+    virtual void TearDown() {
+        mComposerClient->dispose();
+        mBGSurfaceControl = 0;
+        mFGSurfaceControl = 0;
+        mSyncSurfaceControl = 0;
+        mComposerClient = 0;
+    }
+
+    void waitForPostedBuffers() {
+        // Since the sync surface is in synchronous mode (i.e. double buffered)
+        // posting three buffers to it should ensure that at least two
+        // SurfaceFlinger::handlePageFlip calls have been made, which should
+        // guaranteed that a buffer posted to another Surface has been retired.
+        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+    }
+
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<SurfaceControl> mBGSurfaceControl;
+    sp<SurfaceControl> mFGSurfaceControl;
+
+    // This surface is used to ensure that the buffers posted to
+    // mFGSurfaceControl have been picked up by SurfaceFlinger.
+    sp<SurfaceControl> mSyncSurfaceControl;
+};
+
+TEST_F(LayerUpdateTest, LayerMoveWorks) {
+    sp<ScreenCapture> sc;
+    {
+        SCOPED_TRACE("before move");
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(  0,  12,  63,  63, 195);
+        sc->checkPixel( 75,  75, 195,  63,  63);
+        sc->checkPixel(145, 145,  63,  63, 195);
+    }
+
+    SurfaceComposerClient::openGlobalTransaction();
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+    {
+        // This should reflect the new position, but not the new color.
+        SCOPED_TRACE("after move, before redraw");
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel( 24,  24,  63,  63, 195);
+        sc->checkPixel( 75,  75,  63,  63, 195);
+        sc->checkPixel(145, 145, 195,  63,  63);
+    }
+
+    fillSurfaceRGBA8(mFGSurfaceControl, 63, 195, 63);
+    waitForPostedBuffers();
+    {
+        // This should reflect the new position and the new color.
+        SCOPED_TRACE("after redraw");
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel( 24,  24,  63,  63, 195);
+        sc->checkPixel( 75,  75,  63,  63, 195);
+        sc->checkPixel(145, 145,  63, 195,  63);
+    }
+}
+
+TEST_F(LayerUpdateTest, LayerResizeWorks) {
+    sp<ScreenCapture> sc;
+    {
+        SCOPED_TRACE("before resize");
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(  0,  12,  63,  63, 195);
+        sc->checkPixel( 75,  75, 195,  63,  63);
+        sc->checkPixel(145, 145,  63,  63, 195);
+    }
+
+    LOGD("resizing");
+    SurfaceComposerClient::openGlobalTransaction();
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setSize(128, 128));
+    SurfaceComposerClient::closeGlobalTransaction(true);
+    LOGD("resized");
+    {
+        // This should not reflect the new size or color because SurfaceFlinger
+        // has not yet received a buffer of the correct size.
+        SCOPED_TRACE("after resize, before redraw");
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(  0,  12,  63,  63, 195);
+        sc->checkPixel( 75,  75, 195,  63,  63);
+        sc->checkPixel(145, 145,  63,  63, 195);
+    }
+
+    LOGD("drawing");
+    fillSurfaceRGBA8(mFGSurfaceControl, 63, 195, 63);
+    waitForPostedBuffers();
+    LOGD("drawn");
+    {
+        // This should reflect the new size and the new color.
+        SCOPED_TRACE("after redraw");
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel( 24,  24,  63,  63, 195);
+        sc->checkPixel( 75,  75,  63, 195,  63);
+        sc->checkPixel(145, 145,  63, 195,  63);
+    }
+}
+
+}