am 5bfa3a34: am 011b5bcc: Merge "implement part of [3094280] New animation for screen on and screen off add support for screen on animation" into gingerbread

Merge commit '5bfa3a34eaef759c3ec4def76f646eb1c0bf997f'

* commit '5bfa3a34eaef759c3ec4def76f646eb1c0bf997f':
  implement part of [3094280] New animation for screen on and screen off
diff --git a/include/binder/CursorWindow.h b/include/binder/CursorWindow.h
new file mode 100644
index 0000000..4fbff2a
--- /dev/null
+++ b/include/binder/CursorWindow.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+#ifndef _ANDROID__DATABASE_WINDOW_H
+#define _ANDROID__DATABASE_WINDOW_H
+
+#include <cutils/log.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <binder/IMemory.h>
+#include <utils/RefBase.h>
+
+#define DEFAULT_WINDOW_SIZE 4096
+#define MAX_WINDOW_SIZE (1024 * 1024)
+#define WINDOW_ALLOCATION_SIZE 4096
+
+#define ROW_SLOT_CHUNK_NUM_ROWS 16
+
+// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS,
+// with an offset after the rows that points to the next chunk
+#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t))
+
+
+#if LOG_NDEBUG
+
+#define IF_LOG_WINDOW() if (false)
+#define LOG_WINDOW(...)
+
+#else
+
+#define IF_LOG_WINDOW() IF_LOG(LOG_DEBUG, "CursorWindow")
+#define LOG_WINDOW(...) LOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
+
+#endif
+
+
+// When defined to true strings are stored as UTF8, otherwise they're UTF16
+#define WINDOW_STORAGE_UTF8 1
+
+// When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window
+#define WINDOW_STORAGE_INLINE_NUMERICS 1
+
+namespace android {
+
+typedef struct
+{
+    uint32_t numRows;
+    uint32_t numColumns;
+} window_header_t;
+
+typedef struct
+{
+    uint32_t offset;
+} row_slot_t;
+
+typedef struct
+{
+    uint8_t type;
+    union {
+        double d;
+        int64_t l;
+        struct {
+            uint32_t offset;
+            uint32_t size;
+        } buffer;
+    } data;
+} __attribute__((packed)) field_slot_t;
+
+#define FIELD_TYPE_NULL 0
+#define FIELD_TYPE_INTEGER 1
+#define FIELD_TYPE_FLOAT 2
+#define FIELD_TYPE_STRING 3
+#define FIELD_TYPE_BLOB 4
+
+/**
+ * This class stores a set of rows from a database in a buffer. The begining of the
+ * window has first chunk of row_slot_ts, which are offsets to the row directory, followed by
+ * an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case
+ * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
+ * field_slot_t per column, which has the size, offset, and type of the data for that field.
+ * Note that the data types come from sqlite3.h.
+ */
+class CursorWindow
+{
+public:
+                        CursorWindow(size_t maxSize);
+                        CursorWindow(){}
+    bool                setMemory(const sp<IMemory>&);
+                        ~CursorWindow();
+
+    bool                initBuffer(bool localOnly);
+    sp<IMemory>         getMemory() {return mMemory;}
+
+    size_t              size() {return mSize;}
+    uint8_t *           data() {return mData;}
+    uint32_t            getNumRows() {return mHeader->numRows;}
+    uint32_t            getNumColumns() {return mHeader->numColumns;}
+    void                freeLastRow() {
+                            if (mHeader->numRows > 0) {
+                                mHeader->numRows--;
+                            }
+                        }
+    bool                setNumColumns(uint32_t numColumns)
+                            {
+                                uint32_t cur = mHeader->numColumns;
+                                if (cur > 0 && cur != numColumns) {
+                                    LOGE("Trying to go from %d columns to %d", cur, numColumns);
+                                    return false;
+                                }
+                                mHeader->numColumns = numColumns;
+                                return true;
+                            }
+
+    int32_t             freeSpace();
+
+    void                clear();
+
+                        /**
+                         * Allocate a row slot and its directory. The returned
+                         * pointer points to the begining of the row's directory
+                         * or NULL if there wasn't room. The directory is
+                         * initialied with NULL entries for each field.
+                         */
+    field_slot_t *      allocRow();
+
+                        /**
+                         * Allocate a portion of the window. Returns the offset
+                         * of the allocation, or 0 if there isn't enough space.
+                         * If aligned is true, the allocation gets 4 byte alignment.
+                         */
+    uint32_t            alloc(size_t size, bool aligned = false);
+
+    uint32_t            read_field_slot(int row, int column, field_slot_t * slot);
+
+                        /**
+                         * Copy data into the window at the given offset.
+                         */
+    void                copyIn(uint32_t offset, uint8_t const * data, size_t size);
+    void                copyIn(uint32_t offset, int64_t data);
+    void                copyIn(uint32_t offset, double data);
+
+    void                copyOut(uint32_t offset, uint8_t * data, size_t size);
+    int64_t             copyOutLong(uint32_t offset);
+    double              copyOutDouble(uint32_t offset);
+
+    bool                putLong(unsigned int row, unsigned int col, int64_t value);
+    bool                putDouble(unsigned int row, unsigned int col, double value);
+    bool                putNull(unsigned int row, unsigned int col);
+
+    bool                getLong(unsigned int row, unsigned int col, int64_t * valueOut);
+    bool                getDouble(unsigned int row, unsigned int col, double * valueOut);
+    bool                getNull(unsigned int row, unsigned int col, bool * valueOut);
+
+    uint8_t *           offsetToPtr(uint32_t offset) {return mData + offset;}
+
+    row_slot_t *        allocRowSlot();
+
+    row_slot_t *        getRowSlot(int row);
+
+                        /**
+                         * return NULL if Failed to find rowSlot or
+                         * Invalid rowSlot
+                         */
+    field_slot_t *      getFieldSlotWithCheck(int row, int column);
+    field_slot_t *      getFieldSlot(int row, int column)
+                            {
+                                int fieldDirOffset = getRowSlot(row)->offset;
+                                return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
+                            }
+
+private:
+    uint8_t * mData;
+    size_t mSize;
+    size_t mMaxSize;
+    window_header_t * mHeader;
+    sp<IMemory> mMemory;
+
+    /**
+     * Offset of the lowest unused data byte in the array.
+     */
+    uint32_t mFreeOffset;
+};
+
+}; // namespace android
+
+#endif
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index de447be..0c9a072 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -43,6 +43,7 @@
         eSecure             = 0x00000080,
         eNonPremultiplied   = 0x00000100,
         ePushBuffers        = 0x00000200,
+        eOpaque             = 0x00000400,
 
         eFXSurfaceNormal    = 0x00000000,
         eFXSurfaceBlur      = 0x00010000,
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 22684db..cef439c 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -94,7 +94,7 @@
     friend class SurfaceComposerClient;
 
     // camera and camcorder need access to the ISurface binder interface for preview
-    friend class Camera;
+    friend class CameraService;
     friend class MediaRecorder;
     // mediaplayer needs access to ISurface for display
     friend class MediaPlayer;
@@ -173,11 +173,12 @@
      * (eventually this should go away and be replaced by proper APIs)
      */
     // camera and camcorder need access to the ISurface binder interface for preview
-    friend class Camera;
+    friend class CameraService;
     friend class MediaRecorder;
     // MediaPlayer needs access to ISurface for display
     friend class MediaPlayer;
     friend class IOMX;
+    friend class SoftwareRenderer;
     // this is just to be able to write some unit tests
     friend class Test;
 
@@ -314,4 +315,3 @@
 }; // namespace android
 
 #endif // ANDROID_SF_SURFACE_H
-
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index a3e85a9..c446633 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -72,6 +72,9 @@
     GraphicBuffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
             uint32_t stride, native_handle_t* handle, bool keepOwnership);
 
+    // create a buffer from an existing android_native_buffer_t
+    GraphicBuffer(android_native_buffer_t* buffer, bool keepOwnership);
+
     // return status
     status_t initCheck() const;
 
@@ -137,6 +140,10 @@
     GraphicBufferMapper& mBufferMapper;
     ssize_t mInitCheck;
     int mIndex;
+
+    // If we're wrapping another buffer then this reference will make sure it
+    // doesn't get freed.
+    sp<android_native_buffer_t> mWrappedBuffer;
 };
 
 }; // namespace android
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 3599163..63185d3 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -389,6 +389,14 @@
      */
     virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
 
+    /* Transfers touch focus from the window associated with one channel to the
+     * window associated with the other channel.
+     *
+     * Returns true on success.  False if the window did not actually have touch focus.
+     */
+    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
+            const sp<InputChannel>& toChannel) = 0;
+
     /* Registers or unregister input channels that may be used as targets for input events.
      * If monitor is true, the channel will receive a copy of all input events.
      *
@@ -445,6 +453,9 @@
     virtual void setFocusedApplication(const InputApplication* inputApplication);
     virtual void setInputDispatchMode(bool enabled, bool frozen);
 
+    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
+            const sp<InputChannel>& toChannel);
+
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
@@ -746,6 +757,9 @@
         // Clears the current state.
         void clear();
 
+        // Copies pointer-related parts of the input state to another instance.
+        void copyPointerStateTo(InputState& other) const;
+
     private:
         struct KeyMemento {
             int32_t deviceId;
diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h
index 3b975b4..e1ee8eb 100644
--- a/include/utils/Singleton.h
+++ b/include/utils/Singleton.h
@@ -37,6 +37,11 @@
         }
         return *instance;
     }
+
+    static bool hasInstance() {
+        Mutex::Autolock _l(sLock);
+        return sInstance != 0;
+    }
     
 protected:
     ~Singleton() { };
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk
index 13dc500..f9d9f25 100644
--- a/libs/binder/Android.mk
+++ b/libs/binder/Android.mk
@@ -16,6 +16,7 @@
 sources := \
     Binder.cpp \
     BpBinder.cpp \
+    CursorWindow.cpp \
     IInterface.cpp \
     IMemory.cpp \
     IPCThreadState.cpp \
diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp
new file mode 100644
index 0000000..bdd4dd6
--- /dev/null
+++ b/libs/binder/CursorWindow.cpp
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2006-2007 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "CursorWindow"
+
+#include <utils/Log.h>
+#include <binder/CursorWindow.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+namespace android {
+
+CursorWindow::CursorWindow(size_t maxSize) :
+    mMaxSize(maxSize)
+{
+}
+
+bool CursorWindow::setMemory(const sp<IMemory>& memory)
+{
+    mMemory = memory;
+    mData = (uint8_t *) memory->pointer();
+    if (mData == NULL) {
+        return false;
+    }
+    mHeader = (window_header_t *) mData;
+
+    // Make the window read-only
+    ssize_t size = memory->size();
+    mSize = size;
+    mMaxSize = size;
+    mFreeOffset = size;
+LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData);
+    return true;
+}
+
+bool CursorWindow::initBuffer(bool localOnly)
+{
+    //TODO Use a non-memory dealer mmap region for localOnly
+
+    sp<MemoryHeapBase> heap;
+    heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");
+    if (heap != NULL) {
+        mMemory = new MemoryBase(heap, 0, mMaxSize);
+        if (mMemory != NULL) {
+            mData = (uint8_t *) mMemory->pointer();
+            if (mData) {
+                mHeader = (window_header_t *) mData;
+                mSize = mMaxSize;
+
+                // Put the window into a clean state
+                clear();
+            LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData);
+                return true;                
+            }
+        } 
+        LOGE("CursorWindow heap allocation failed");
+        return false;
+    } else {
+        LOGE("failed to create the CursorWindow heap");
+        return false;
+    }
+}
+
+CursorWindow::~CursorWindow()
+{
+    // Everything that matters is a smart pointer
+}
+
+void CursorWindow::clear()
+{
+    mHeader->numRows = 0;
+    mHeader->numColumns = 0;
+    mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE;
+    // Mark the first chunk's next 'pointer' as null
+    *((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0;
+}
+
+int32_t CursorWindow::freeSpace()
+{
+    int32_t freeSpace = mSize - mFreeOffset;
+    if (freeSpace < 0) {
+        freeSpace = 0;
+    }
+    return freeSpace;
+}
+
+field_slot_t * CursorWindow::allocRow()
+{
+    // Fill in the row slot
+    row_slot_t * rowSlot = allocRowSlot();
+    if (rowSlot == NULL) {
+        return NULL;
+    }
+
+    // Allocate the slots for the field directory
+    size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t);
+    uint32_t fieldDirOffset = alloc(fieldDirSize);
+    if (!fieldDirOffset) {
+        mHeader->numRows--;
+        LOGE("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows);
+        return NULL;
+    }
+    field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset);
+    memset(fieldDir, 0x0, fieldDirSize);
+
+LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset);
+    rowSlot->offset = fieldDirOffset;
+
+    return fieldDir;
+}
+
+uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned)
+{
+    int32_t size;
+    uint32_t padding;
+    if (aligned) {
+        // 4 byte alignment
+        padding = 4 - (mFreeOffset & 0x3);
+    } else {
+        padding = 0;
+    }
+
+    size = requestedSize + padding;
+
+    if (size > freeSpace()) {
+        LOGV("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size,
+                freeSpace(), mHeader->numRows);
+        // Only grow the window if the first row doesn't fit
+        if (mHeader->numRows > 1) {
+            LOGV("not growing since there are already %d row(s), max size %d", mHeader->numRows,
+                    mMaxSize);
+            return 0;
+        }
+
+        // Find a new size that will fit the allocation
+        int allocated = mSize - freeSpace();
+        int newSize = mSize + WINDOW_ALLOCATION_SIZE;
+        while (size > (newSize - allocated)) {
+            newSize += WINDOW_ALLOCATION_SIZE;
+            if (newSize > mMaxSize) {
+                LOGE("Attempting to grow window beyond max size (%d)", mMaxSize);
+                return 0;
+            }
+        }
+LOG_WINDOW("found size %d", newSize);
+        mSize = newSize;
+    }
+
+    uint32_t offset = mFreeOffset + padding;
+    mFreeOffset += size;
+    return offset;
+}
+
+row_slot_t * CursorWindow::getRowSlot(int row)
+{
+    LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row);
+    int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS;
+    int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS;
+    int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
+    uint8_t * rowChunk = mData + sizeof(window_header_t);
+    for (int i = 0; i < chunkNum; i++) {
+        rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset)));
+        chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
+    }
+    return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
+    LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row);    
+}
+
+row_slot_t * CursorWindow::allocRowSlot()
+{
+    int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS;
+    int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS;
+    int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
+    uint8_t * rowChunk = mData + sizeof(window_header_t);
+LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos);
+    for (int i = 0; i < chunkNum; i++) {
+        uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset));
+LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset);
+        if (nextChunkOffset == 0) {
+            // Allocate a new row chunk
+            nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true);
+            if (nextChunkOffset == 0) {
+                return NULL;
+            }
+            rowChunk = offsetToPtr(nextChunkOffset);
+LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk);
+            *((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData;
+            // Mark the new chunk's next 'pointer' as null
+            *((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0;
+        } else {
+LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset);
+            rowChunk = offsetToPtr(nextChunkOffset);
+            chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
+        }
+    }
+    mHeader->numRows++;
+
+    return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
+}
+
+field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
+{
+  if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
+      LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
+      return NULL;
+  }        
+  row_slot_t * rowSlot = getRowSlot(row);
+  if (!rowSlot) {
+      LOGE("Failed to find rowSlot for row %d", row);
+      return NULL;
+  }
+  if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
+      LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
+      return NULL;
+  }  
+  int fieldDirOffset = rowSlot->offset;
+  return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;  
+}
+
+uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut)
+{
+    if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
+        LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
+        return -1;
+    }        
+    row_slot_t * rowSlot = getRowSlot(row);
+    if (!rowSlot) {
+        LOGE("Failed to find rowSlot for row %d", row);
+        return -1;
+    }
+    if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
+        LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
+        return -1;
+    }
+LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset);
+    field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(rowSlot->offset);
+LOG_WINDOW("Read field_slot_t %d,%d: offset = %d, size = %d, type = %d", row, column, fieldDir[column].data.buffer.offset, fieldDir[column].data.buffer.size, fieldDir[column].type);
+
+    // Copy the data to the out param
+    slotOut->data.buffer.offset = fieldDir[column].data.buffer.offset;
+    slotOut->data.buffer.size = fieldDir[column].data.buffer.size;
+    slotOut->type = fieldDir[column].type;
+    return 0;
+}
+
+void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size)
+{
+    assert(offset + size <= mSize);    
+    memcpy(mData + offset, data, size);
+}
+
+void CursorWindow::copyIn(uint32_t offset, int64_t data)
+{
+    assert(offset + sizeof(int64_t) <= mSize);
+    memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t));
+}
+
+void CursorWindow::copyIn(uint32_t offset, double data)
+{
+    assert(offset + sizeof(double) <= mSize);
+    memcpy(mData + offset, (uint8_t *)&data, sizeof(double));
+}
+
+void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size)
+{
+    assert(offset + size <= mSize);
+    memcpy(data, mData + offset, size);
+}
+
+int64_t CursorWindow::copyOutLong(uint32_t offset)
+{
+    int64_t value;
+    assert(offset + sizeof(int64_t) <= mSize);
+    memcpy(&value, mData + offset, sizeof(int64_t));
+    return value;
+}
+
+double CursorWindow::copyOutDouble(uint32_t offset)
+{
+    double value;
+    assert(offset + sizeof(double) <= mSize);
+    memcpy(&value, mData + offset, sizeof(double));
+    return value;
+}
+
+bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot) {
+        return false;
+    }
+
+#if WINDOW_STORAGE_INLINE_NUMERICS
+    fieldSlot->data.l = value;
+#else
+    int offset = alloc(sizeof(int64_t));
+    if (!offset) {
+        return false;
+    }
+
+    copyIn(offset, value);
+
+    fieldSlot->data.buffer.offset = offset;
+    fieldSlot->data.buffer.size = sizeof(int64_t);
+#endif
+    fieldSlot->type = FIELD_TYPE_INTEGER;
+    return true;
+}
+
+bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot) {
+        return false;
+    }
+
+#if WINDOW_STORAGE_INLINE_NUMERICS
+    fieldSlot->data.d = value;
+#else
+    int offset = alloc(sizeof(int64_t));
+    if (!offset) {
+        return false;
+    }
+
+    copyIn(offset, value);
+
+    fieldSlot->data.buffer.offset = offset;
+    fieldSlot->data.buffer.size = sizeof(double);
+#endif
+    fieldSlot->type = FIELD_TYPE_FLOAT;
+    return true;
+}
+
+bool CursorWindow::putNull(unsigned int row, unsigned int col)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot) {
+        return false;
+    }
+
+    fieldSlot->type = FIELD_TYPE_NULL;
+    fieldSlot->data.buffer.offset = 0;
+    fieldSlot->data.buffer.size = 0;
+    return true;
+}
+
+bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) {
+        return false;
+    }
+    
+#if WINDOW_STORAGE_INLINE_NUMERICS
+    *valueOut = fieldSlot->data.l;
+#else
+    *valueOut = copyOutLong(fieldSlot->data.buffer.offset);
+#endif
+    return true;
+}
+
+bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) {
+        return false;
+    }
+
+#if WINDOW_STORAGE_INLINE_NUMERICS
+    *valueOut = fieldSlot->data.d;
+#else
+    *valueOut = copyOutDouble(fieldSlot->data.buffer.offset);
+#endif
+    return true;
+}
+
+bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot) {
+        return false;
+    }
+    
+    if (fieldSlot->type != FIELD_TYPE_NULL) {
+        *valueOut = false;
+    } else {
+        *valueOut = true;
+    }
+    return true;
+}
+
+}; // namespace android
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 4bc5d9e..8f583f0 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -380,11 +380,6 @@
 {
     SharedBufferStack& stack( *mSharedStack );
 
-    if (stack.head == tail && stack.available == mNumBuffers) {
-        LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d",
-                tail, stack.head, stack.available, stack.queued);
-    }
-
     RWLock::AutoRLock _rd(mLock);
 
     const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD);
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index d44aab9..0994980 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -364,6 +364,13 @@
         height   = surface->mHeight;
         format   = surface->mFormat;
         flags    = surface->mFlags;
+    } else if (surface != 0 && surface->mSurface != 0) {
+        LOGW("Parceling invalid surface with non-NULL ISurface as NULL: "
+             "mSurface = %p, mIdentity = %d, mWidth = %d, mHeight = %d, "
+             "mFormat = %d, mFlags = 0x%08x, mInitCheck = %d",
+             surface->mSurface.get(), surface->mIdentity, surface->mWidth,
+             surface->mHeight, surface->mFormat, surface->mFlags,
+             surface->mInitCheck);
     }
     parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
     parcel->writeInt32(identity);
@@ -438,21 +445,15 @@
             mSharedBufferClient = new SharedBufferClient(
                     mClient.getSharedClient(), token, 2, mIdentity);
             mInitCheck = mClient.getSharedClient()->validate(token);
+        } else {
+            LOGW("Not initializing the shared buffer client because token = %d",
+                    token);
         }
     }
 }
 
 Surface::~Surface()
 {
-    // this is a client-side operation, the surface is destroyed, unmap
-    // its buffers in this process.
-    size_t size = mBuffers.size();
-    for (size_t i=0 ; i<size ; i++) {
-        if (mBuffers[i] != 0 && mBuffers[i]->handle != 0) {
-            getBufferMapper().unregisterBuffer(mBuffers[i]->handle);
-        }
-    }
-
     // clear all references and trigger an IPC now, to make sure things
     // happen without delay, since these resources are quite heavy.
     mBuffers.clear();
@@ -1011,7 +1012,20 @@
 
 int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const
 {
-    return buffer->getIndex();
+    int idx = buffer->getIndex();
+    if (idx < 0) {
+        // The buffer doesn't have an index set.  See if the handle the same as
+        // one of the buffers for which we do know the index.  This can happen
+        // e.g. if GraphicBuffer is used to wrap an android_native_buffer_t that
+        // was dequeued from an ANativeWindow.
+        for (int i = 0; i < mBuffers.size(); i++) {
+            if (buffer->handle == mBuffers[i]->handle) {
+                idx = mBuffers[i]->getIndex();
+                break;
+            }
+        }
+    }
+    return idx;
 }
 
 status_t Surface::getBufferLocked(int index,
@@ -1025,7 +1039,6 @@
     // free the current buffer
     sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index));
     if (currentBuffer != 0) {
-        getBufferMapper().unregisterBuffer(currentBuffer->handle);
         currentBuffer.clear();
     }
 
@@ -1033,7 +1046,7 @@
     LOGE_IF(buffer==0,
             "ISurface::getBuffer(%d, %08x) returned NULL",
             index, usage);
-    if (buffer != 0) { // this should never happen by construction
+    if (buffer != 0) { // this should always happen by construction
         LOGE_IF(buffer->handle == NULL, 
                 "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) "
                 "returned a buffer with a null handle",
@@ -1041,13 +1054,8 @@
         err = mSharedBufferClient->getStatus();
         LOGE_IF(err,  "Surface (identity=%d) state = %d", mIdentity, err);
         if (!err && buffer->handle != NULL) {
-            err = getBufferMapper().registerBuffer(buffer->handle);
-            LOGW_IF(err, "registerBuffer(...) failed %d (%s)",
-                    err, strerror(-err));
-            if (err == NO_ERROR) {
-                currentBuffer = buffer;
-                currentBuffer->setIndex(index);
-            }
+            currentBuffer = buffer;
+            currentBuffer->setIndex(index);
         } else {
             err = err<0 ? err : status_t(NO_MEMORY);
         }
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 519c277..436e064 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -77,6 +77,19 @@
     handle = inHandle;
 }
 
+GraphicBuffer::GraphicBuffer(android_native_buffer_t* buffer, bool keepOwnership)
+    : BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
+      mBufferMapper(GraphicBufferMapper::get()),
+      mInitCheck(NO_ERROR), mIndex(-1), mWrappedBuffer(buffer)
+{
+    width  = buffer->width;
+    height = buffer->height;
+    stride = buffer->stride;
+    format = buffer->format;
+    usage  = buffer->usage;
+    handle = buffer->handle;
+}
+
 GraphicBuffer::~GraphicBuffer()
 {
     if (handle) {
@@ -87,12 +100,14 @@
 void GraphicBuffer::free_handle()
 {
     if (mOwner == ownHandle) {
+        mBufferMapper.unregisterBuffer(handle);
         native_handle_close(handle);
         native_handle_delete(const_cast<native_handle*>(handle));
     } else if (mOwner == ownData) {
         GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
         allocator.free(handle);
     }
+    mWrappedBuffer = 0;
 }
 
 status_t GraphicBuffer::initCheck() const {
@@ -253,6 +268,11 @@
     }
 
     mOwner = ownHandle;
+
+    if (handle != 0) {
+        mBufferMapper.registerBuffer(handle);
+    }
+
     return NO_ERROR;
 }
 
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 6ba19d7..84c3db8 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -2612,6 +2612,82 @@
     }
 }
 
+bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
+        const sp<InputChannel>& toChannel) {
+#if DEBUG_FOCUS
+    LOGD("transferTouchFocus: fromChannel=%s, toChannel=%s",
+            fromChannel->getName().string(), toChannel->getName().string());
+#endif
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        const InputWindow* fromWindow = getWindowLocked(fromChannel);
+        const InputWindow* toWindow = getWindowLocked(toChannel);
+        if (! fromWindow || ! toWindow) {
+#if DEBUG_FOCUS
+            LOGD("Cannot transfer focus because from or to window not found.");
+#endif
+            return false;
+        }
+        if (fromWindow == toWindow) {
+#if DEBUG_FOCUS
+            LOGD("Trivial transfer to same window.");
+#endif
+            return true;
+        }
+
+        bool found = false;
+        for (size_t i = 0; i < mTouchState.windows.size(); i++) {
+            const TouchedWindow& touchedWindow = mTouchState.windows[i];
+            if (touchedWindow.window == fromWindow) {
+                int32_t oldTargetFlags = touchedWindow.targetFlags;
+                BitSet32 pointerIds = touchedWindow.pointerIds;
+
+                mTouchState.windows.removeAt(i);
+
+                int32_t newTargetFlags = 0;
+                if (oldTargetFlags & InputTarget::FLAG_FOREGROUND) {
+                    newTargetFlags |= InputTarget::FLAG_FOREGROUND;
+                    if (toWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH) {
+                        newTargetFlags |= InputTarget::FLAG_SPLIT;
+                    }
+                }
+                mTouchState.addOrUpdateWindow(toWindow, newTargetFlags, pointerIds);
+
+                found = true;
+                break;
+            }
+        }
+
+        if (! found) {
+#if DEBUG_FOCUS
+            LOGD("Focus transfer failed because from window did not have focus.");
+#endif
+            return false;
+        }
+
+        ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel);
+        ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel);
+        if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) {
+            sp<Connection> fromConnection = mConnectionsByReceiveFd.valueAt(fromConnectionIndex);
+            sp<Connection> toConnection = mConnectionsByReceiveFd.valueAt(toConnectionIndex);
+
+            fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
+            synthesizeCancelationEventsForConnectionLocked(fromConnection,
+                    InputState::CANCEL_POINTER_EVENTS,
+                    "transferring touch focus from this window to another window");
+        }
+
+#if DEBUG_FOCUS
+        logDispatchStateLocked();
+#endif
+    } // release lock
+
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mLooper->wake();
+    return true;
+}
+
 void InputDispatcher::logDispatchStateLocked() {
     String8 dump;
     dumpDispatchStateLocked(dump);
@@ -3338,6 +3414,24 @@
     mMotionMementos.clear();
 }
 
+void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
+    for (size_t i = 0; i < mMotionMementos.size(); i++) {
+        const MotionMemento& memento = mMotionMementos.itemAt(i);
+        if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
+            for (size_t j = 0; j < other.mMotionMementos.size(); ) {
+                const MotionMemento& otherMemento = other.mMotionMementos.itemAt(j);
+                if (memento.deviceId == otherMemento.deviceId
+                        && memento.source == otherMemento.source) {
+                    other.mMotionMementos.removeAt(j);
+                } else {
+                    j += 1;
+                }
+            }
+            other.mMotionMementos.push(memento);
+        }
+    }
+}
+
 bool InputDispatcher::InputState::shouldCancelEvent(int32_t eventSource,
         CancelationOptions options) {
     switch (options) {
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 8345cc3..91e7df3 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -317,6 +317,12 @@
             mStringPoolSize =
                 (mHeader->header.size-mHeader->stringsStart)/charSize;
         } else {
+            // check invariant: styles starts before end of data
+            if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) {
+                LOGW("Bad style block: style block starts at %d past data size of %d\n",
+                    (int)mHeader->stylesStart, (int)mHeader->header.size);
+                return (mError=BAD_TYPE);
+            }
             // check invariant: styles follow the strings
             if (mHeader->stylesStart <= mHeader->stringsStart) {
                 LOGW("Bad style block: style block starts at %d, before strings at %d\n",
@@ -1878,6 +1884,12 @@
         outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen);
         outName->name = grp->basePackage->keyStrings.stringAt(
             dtohl(entry->key.index), &outName->nameLen);
+
+        // If we have a bad index for some reason, we should abort.
+        if (outName->type == NULL || outName->name == NULL) {
+            return false;
+        }
+
         return true;
     }
 
@@ -2609,6 +2621,24 @@
         *outType = *defType;
     }
     *outName = String16(p, end-p);
+    if(**outPackage == 0) {
+        if(outErrorMsg) {
+            *outErrorMsg = "Resource package cannot be an empty string";
+        }
+        return false;
+    }
+    if(**outType == 0) {
+        if(outErrorMsg) {
+            *outErrorMsg = "Resource type cannot be an empty string";
+        }
+        return false;
+    }
+    if(**outName == 0) {
+        if(outErrorMsg) {
+            *outErrorMsg = "Resource id cannot be an empty string";
+        }
+        return false;
+    }
     return true;
 }
 
@@ -4127,13 +4157,16 @@
                                     | (0x00ff0000 & ((typeIndex+1)<<16))
                                     | (0x0000ffff & (entryIndex));
                         resource_name resName;
-                        this->getResourceName(resID, &resName);
-                        printf("      spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
-                            resID,
-                            CHAR16_TO_CSTR(resName.package, resName.packageLen),
-                            CHAR16_TO_CSTR(resName.type, resName.typeLen),
-                            CHAR16_TO_CSTR(resName.name, resName.nameLen),
-                            dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+                        if (this->getResourceName(resID, &resName)) {
+                            printf("      spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
+                                resID,
+                                CHAR16_TO_CSTR(resName.package, resName.packageLen),
+                                CHAR16_TO_CSTR(resName.type, resName.typeLen),
+                                CHAR16_TO_CSTR(resName.name, resName.nameLen),
+                                dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+                        } else {
+                            printf("      INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
+                        }
                     }
                 }
                 for (size_t configIndex=0; configIndex<NTC; configIndex++) {
@@ -4340,11 +4373,14 @@
                                     | (0x00ff0000 & ((typeIndex+1)<<16))
                                     | (0x0000ffff & (entryIndex));
                         resource_name resName;
-                        this->getResourceName(resID, &resName);
-                        printf("        resource 0x%08x %s:%s/%s: ", resID,
-                                CHAR16_TO_CSTR(resName.package, resName.packageLen),
-                                CHAR16_TO_CSTR(resName.type, resName.typeLen),
-                                CHAR16_TO_CSTR(resName.name, resName.nameLen));
+                        if (this->getResourceName(resID, &resName)) {
+                            printf("        resource 0x%08x %s:%s/%s: ", resID,
+                                    CHAR16_TO_CSTR(resName.package, resName.packageLen),
+                                    CHAR16_TO_CSTR(resName.type, resName.typeLen),
+                                    CHAR16_TO_CSTR(resName.name, resName.nameLen));
+                        } else {
+                            printf("        INVALID RESOURCE 0x%08x: ", resID);
+                        }
                         if ((thisOffset&0x3) != 0) {
                             printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset);
                             continue;
@@ -4402,18 +4438,19 @@
                                 print_value(pkg, value);
                             } else if (bagPtr != NULL) {
                                 const int N = dtohl(bagPtr->count);
-                                const ResTable_map* mapPtr = (const ResTable_map*)
-                                        (((const uint8_t*)ent) + esize);
+                                const uint8_t* baseMapPtr = (const uint8_t*)ent;
+                                size_t mapOffset = esize;
+                                const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
                                 printf("          Parent=0x%08x, Count=%d\n",
                                     dtohl(bagPtr->parent.ident), N);
-                                for (int i=0; i<N; i++) {
+                                for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
                                     printf("          #%i (Key=0x%08x): ",
                                         i, dtohl(mapPtr->name.ident));
                                     value.copyFrom_dtoh(mapPtr->value);
                                     print_value(pkg, value);
                                     const size_t size = dtohs(mapPtr->value.size);
-                                    mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr)
-                                            + size + sizeof(*mapPtr)-sizeof(mapPtr->value));
+                                    mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
+                                    mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
                                 }
                             }
                         }
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 460b74f..239dc05 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -1969,7 +1969,7 @@
     if (egl_display_t::is_valid(dpy) == EGL_FALSE)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
     // TODO: eglSwapInterval()
-    return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    return EGL_TRUE;
 }
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 2d1a278..bc944a0 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -59,6 +59,8 @@
         "EGL_KHR_image "
         "EGL_KHR_image_base "
         "EGL_KHR_image_pixmap "
+        "EGL_KHR_gl_texture_2D_image "
+        "EGL_KHR_fence_sync "
         "EGL_ANDROID_image_native_buffer "
         "EGL_ANDROID_swap_rectangle "
         ;
@@ -243,9 +245,23 @@
     EGLImageKHR images[IMPL_NUM_IMPLEMENTATIONS];
 };
 
+struct egl_sync_t : public egl_object_t
+{
+    typedef egl_object_t::LocalRef<egl_sync_t, EGLSyncKHR> Ref;
+
+    egl_sync_t(EGLDisplay dpy, EGLContext context, EGLSyncKHR sync)
+        : dpy(dpy), context(context), sync(sync)
+    {
+    }
+    EGLDisplay dpy;
+    EGLContext context;
+    EGLSyncKHR sync;
+};
+
 typedef egl_surface_t::Ref  SurfaceRef;
 typedef egl_context_t::Ref  ContextRef;
 typedef egl_image_t::Ref    ImageRef;
+typedef egl_sync_t::Ref     SyncRef;
 
 struct tls_t
 {
@@ -483,6 +499,11 @@
     return egl_to_native_cast<egl_image_t>(image);
 }
 
+static inline
+egl_sync_t* get_sync(EGLSyncKHR sync) {
+    return egl_to_native_cast<egl_sync_t>(sync);
+}
+
 static egl_connection_t* validate_display_config(
         EGLDisplay dpy, EGLConfig config,
         egl_display_t const*& dp)
@@ -1790,6 +1811,111 @@
      return EGL_TRUE;
 }
 
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 5
+// ----------------------------------------------------------------------------
+
+
+EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
+{
+    EGLContext ctx = eglGetCurrentContext();
+    ContextRef _c(ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_NO_SYNC_KHR);
+    if (!validate_display_context(dpy, ctx))
+        return EGL_NO_SYNC_KHR;
+    egl_display_t const * const dp = get_display(dpy);
+    egl_context_t * const c = get_context(ctx);
+    EGLSyncKHR result = EGL_NO_SYNC_KHR;
+    if (c->cnx->egl.eglCreateSyncKHR) {
+        EGLSyncKHR sync = c->cnx->egl.eglCreateSyncKHR(
+                dp->disp[c->impl].dpy, type, attrib_list);
+        if (sync == EGL_NO_SYNC_KHR)
+            return sync;
+        result = (egl_sync_t*)new egl_sync_t(dpy, ctx, sync);
+    }
+    return (EGLSyncKHR)result;
+}
+
+EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
+{
+    egl_display_t const * const dp = get_display(dpy);
+    if (dp == 0) {
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    }
+
+    SyncRef _s(sync);
+    if (!_s.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    egl_sync_t* syncObject = get_sync(sync);
+
+    EGLContext ctx = syncObject->context;
+    ContextRef _c(ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+    if (!validate_display_context(dpy, ctx))
+        return EGL_FALSE;
+
+    egl_context_t * const c = get_context(ctx);
+
+    if (c->cnx->egl.eglDestroySyncKHR) {
+        return c->cnx->egl.eglDestroySyncKHR(
+                dp->disp[c->impl].dpy, syncObject->sync);
+    }
+
+    return EGL_FALSE;
+}
+
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout)
+{
+    egl_display_t const * const dp = get_display(dpy);
+    if (dp == 0) {
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    }
+
+    SyncRef _s(sync);
+    if (!_s.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    egl_sync_t* syncObject = get_sync(sync);
+
+    EGLContext ctx = syncObject->context;
+    ContextRef _c(ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+    if (!validate_display_context(dpy, ctx))
+        return EGL_FALSE;
+
+    egl_context_t * const c = get_context(ctx);
+
+    if (c->cnx->egl.eglClientWaitSyncKHR) {
+        return c->cnx->egl.eglClientWaitSyncKHR(
+                dp->disp[c->impl].dpy, syncObject->sync, flags, timeout);
+    }
+
+    return EGL_FALSE;
+}
+
+EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value)
+{
+    egl_display_t const * const dp = get_display(dpy);
+    if (dp == 0) {
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    }
+
+    SyncRef _s(sync);
+    if (!_s.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    egl_sync_t* syncObject = get_sync(sync);
+
+    EGLContext ctx = syncObject->context;
+    ContextRef _c(ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+    if (!validate_display_context(dpy, ctx))
+        return EGL_FALSE;
+
+    egl_context_t * const c = get_context(ctx);
+
+    if (c->cnx->egl.eglGetSyncAttribKHR) {
+        return c->cnx->egl.eglGetSyncAttribKHR(
+                dp->disp[c->impl].dpy, syncObject->sync, attribute, value);
+    }
+
+    return EGL_FALSE;
+}
 
 // ----------------------------------------------------------------------------
 // ANDROID extensions
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 5d89287..63c3c19 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -51,6 +51,13 @@
 EGL_ENTRY(EGLImageKHR, eglCreateImageKHR,   EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint *)
 EGL_ENTRY(EGLBoolean,  eglDestroyImageKHR,  EGLDisplay, EGLImageKHR)
 
+/* EGL_EGLEXT_VERSION 5 */
+
+EGL_ENTRY(EGLSyncKHR,   eglCreateSyncKHR,       EGLDisplay, EGLenum, const EGLint *)
+EGL_ENTRY(EGLBoolean,   eglDestroySyncKHR,      EGLDisplay, EGLSyncKHR)
+EGL_ENTRY(EGLint,       eglClientWaitSyncKHR,   EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean,   eglGetSyncAttribKHR,    EGLDisplay, EGLSyncKHR, EGLint, EGLint *)
+
 /* ANDROID extensions */
 
 EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint)
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 18dd483..fee4609 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -39,6 +39,8 @@
 #undef CALL_GL_API
 #undef CALL_GL_API_RETURN
 
+#define DEBUG_CALL_GL_API 0
+
 #if USE_FAST_TLS_KEY
 
     #ifdef HAVE_ARM_TLS_REGISTER
@@ -74,10 +76,24 @@
 
     #define API_ENTRY(_api) _api
 
+#if DEBUG_CALL_GL_API
+
     #define CALL_GL_API(_api, ...)                                       \
         gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;  \
-        _c->_api(__VA_ARGS__)
-    
+        _c->_api(__VA_ARGS__); \
+        GLenum status = GL_NO_ERROR; \
+        while ((status = glGetError()) != GL_NO_ERROR) { \
+            LOGD("[" #_api "] 0x%x", status); \
+        }
+
+#else
+
+    #define CALL_GL_API(_api, ...)                                       \
+        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;  \
+        _c->_api(__VA_ARGS__);
+
+#endif
+
     #define CALL_GL_API_RETURN(_api, ...)                                \
         gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;  \
         return _c->_api(__VA_ARGS__)
diff --git a/opengl/tests/gl_perf/Android.mk b/opengl/tests/gl_perf/Android.mk
new file mode 100644
index 0000000..37647ca
--- /dev/null
+++ b/opengl/tests/gl_perf/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	gl2_perf.cpp \
+	filltest.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+    libEGL \
+    libGLESv2 \
+    libui
+
+LOCAL_MODULE:= test-opengl-gl2_perf
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
+
+include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/gl_perf/fill_common.cpp b/opengl/tests/gl_perf/fill_common.cpp
new file mode 100644
index 0000000..a069f67
--- /dev/null
+++ b/opengl/tests/gl_perf/fill_common.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2007 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 "fragment_shaders.cpp"
+
+FILE * fOut = NULL;
+void ptSwap();
+
+static char gCurrentTestName[1024];
+static uint32_t gWidth = 0;
+static uint32_t gHeight = 0;
+
+static void checkGlError(const char* op) {
+    for (GLint error = glGetError(); error; error
+            = glGetError()) {
+        LOGE("after %s() glError (0x%x)\n", op, error);
+    }
+}
+
+GLuint loadShader(GLenum shaderType, const char* pSource) {
+    GLuint shader = glCreateShader(shaderType);
+    if (shader) {
+        glShaderSource(shader, 1, &pSource, NULL);
+        glCompileShader(shader);
+        GLint compiled = 0;
+        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+        if (!compiled) {
+            GLint infoLen = 0;
+            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+            if (infoLen) {
+                char* buf = (char*) malloc(infoLen);
+                if (buf) {
+                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
+                    LOGE("Could not compile shader %d:\n%s\n", shaderType, buf);
+                    free(buf);
+                }
+                glDeleteShader(shader);
+                shader = 0;
+            }
+        }
+    }
+    return shader;
+}
+
+enum {
+    A_POS,
+    A_COLOR,
+    A_TEX0,
+    A_TEX1
+};
+
+GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
+    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
+    if (!vertexShader) {
+        return 0;
+    }
+
+    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
+    if (!pixelShader) {
+        return 0;
+    }
+
+    GLuint program = glCreateProgram();
+    if (program) {
+        glAttachShader(program, vertexShader);
+        checkGlError("glAttachShader v");
+        glAttachShader(program, pixelShader);
+        checkGlError("glAttachShader p");
+
+        glBindAttribLocation(program, A_POS, "a_pos");
+        glBindAttribLocation(program, A_COLOR, "a_color");
+        glBindAttribLocation(program, A_TEX0, "a_tex0");
+        glBindAttribLocation(program, A_TEX1, "a_tex1");
+        glLinkProgram(program);
+        GLint linkStatus = GL_FALSE;
+        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+        if (linkStatus != GL_TRUE) {
+            GLint bufLength = 0;
+            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+            if (bufLength) {
+                char* buf = (char*) malloc(bufLength);
+                if (buf) {
+                    glGetProgramInfoLog(program, bufLength, NULL, buf);
+                    LOGE("Could not link program:\n%s\n", buf);
+                    free(buf);
+                }
+            }
+            glDeleteProgram(program);
+            program = 0;
+        }
+    }
+    checkGlError("createProgram");
+    glUseProgram(program);
+    return program;
+}
+
+uint64_t getTime() {
+    struct timespec t;
+    clock_gettime(CLOCK_MONOTONIC, &t);
+    return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000);
+}
+
+uint64_t gTime;
+void startTimer() {
+    gTime = getTime();
+}
+
+
+static void endTimer(int count) {
+    uint64_t t2 = getTime();
+    double delta = ((double)(t2 - gTime)) / 1000000000;
+    double pixels = (gWidth * gHeight) * count;
+    double mpps = pixels / delta / 1000000;
+    double dc60 = ((double)count) / delta / 60;
+
+    if (fOut) {
+        fprintf(fOut, "%s, %f, %f\r\n", gCurrentTestName, mpps, dc60);
+        fflush(fOut);
+    } else {
+        printf("%s, %f, %f\n", gCurrentTestName, mpps, dc60);
+    }
+    LOGI("%s, %f, %f\r\n", gCurrentTestName, mpps, dc60);
+}
+
+
+static const char gVertexShader[] =
+    "attribute vec4 a_pos;\n"
+    "attribute vec4 a_color;\n"
+    "attribute vec2 a_tex0;\n"
+    "attribute vec2 a_tex1;\n"
+    "varying vec4 v_color;\n"
+    "varying vec2 v_tex0;\n"
+    "varying vec2 v_tex1;\n"
+    "uniform vec2 u_texOff;\n"
+
+    "void main() {\n"
+    "    v_color = a_color;\n"
+    "    v_tex0 = a_tex0;\n"
+    "    v_tex1 = a_tex1;\n"
+    "    v_tex0.x += u_texOff.x;\n"
+    "    v_tex1.y += u_texOff.y;\n"
+    "    gl_Position = a_pos;\n"
+    "}\n";
+
+static void setupVA() {
+    static const float vtx[] = {
+        -1.0f,-1.0f,
+         1.0f,-1.0f,
+        -1.0f, 1.0f,
+         1.0f, 1.0f };
+    static const float color[] = {
+        1.0f,0.0f,1.0f,1.0f,
+        0.0f,0.0f,1.0f,1.0f,
+        1.0f,1.0f,0.0f,1.0f,
+        1.0f,1.0f,1.0f,1.0f };
+    static const float tex0[] = {
+        0.0f,0.0f,
+        1.0f,0.0f,
+        0.0f,1.0f,
+        1.0f,1.0f };
+    static const float tex1[] = {
+        1.0f,0.0f,
+        1.0f,1.0f,
+        0.0f,1.0f,
+        0.0f,0.0f };
+
+    glEnableVertexAttribArray(A_POS);
+    glEnableVertexAttribArray(A_COLOR);
+    glEnableVertexAttribArray(A_TEX0);
+    glEnableVertexAttribArray(A_TEX1);
+
+    glVertexAttribPointer(A_POS, 2, GL_FLOAT, false, 8, vtx);
+    glVertexAttribPointer(A_COLOR, 4, GL_FLOAT, false, 16, color);
+    glVertexAttribPointer(A_TEX0, 2, GL_FLOAT, false, 8, tex0);
+    glVertexAttribPointer(A_TEX1, 2, GL_FLOAT, false, 8, tex1);
+}
+
+static void randUniform(int pgm, const char *var) {
+    int loc = glGetUniformLocation(pgm, var);
+    if (loc >= 0) {
+        float x = ((float)rand()) / RAND_MAX;
+        float y = ((float)rand()) / RAND_MAX;
+        float z = ((float)rand()) / RAND_MAX;
+        float w = ((float)rand()) / RAND_MAX;
+        glUniform4f(loc, x, y, z, w);
+    }
+}
+
+static void doLoop(bool warmup, int pgm, uint32_t passCount) {
+    if (warmup) {
+        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+        ptSwap();
+        glFinish();
+        return;
+    }
+
+    startTimer();
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    for (uint32_t ct=0; ct < passCount; ct++) {
+        int loc = glGetUniformLocation(pgm, "u_texOff");
+        glUniform2f(loc, ((float)ct) / passCount, ((float)ct) / 2.f / passCount);
+
+        randUniform(pgm, "u_color");
+        randUniform(pgm, "u_0");
+        randUniform(pgm, "u_1");
+        randUniform(pgm, "u_2");
+        randUniform(pgm, "u_3");
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    }
+    ptSwap();
+    glFinish();
+    endTimer(passCount);
+}
+
+
+static uint32_t rgb(uint32_t r, uint32_t g, uint32_t b)
+{
+    uint32_t ret = 0xff000000;
+    ret |= r & 0xff;
+    ret |= (g & 0xff) << 8;
+    ret |= (b & 0xff) << 16;
+    return ret;
+}
+
+void genTextures() {
+    uint32_t *m = (uint32_t *)malloc(1024*1024*4);
+    for (int y=0; y < 1024; y++){
+        for (int x=0; x < 1024; x++){
+            m[y*1024 + x] = rgb(x, (((x+y) & 0xff) == 0x7f) * 0xff, y);
+        }
+    }
+    glBindTexture(GL_TEXTURE_2D, 1);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, m);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+    for (int y=0; y < 16; y++){
+        for (int x=0; x < 16; x++){
+            m[y*16 + x] = rgb(x << 4, (((x+y) & 0xf) == 0x7) * 0xff, y << 4);
+        }
+    }
+    glBindTexture(GL_TEXTURE_2D, 2);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, m);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    free(m);
+}
+
+static void doSingleTest(uint32_t pgmNum, int tex) {
+    const char *pgmTxt = gFragmentTests[pgmNum]->txt;
+    int pgm = createProgram(gVertexShader, pgmTxt);
+    if (!pgm) {
+        printf("error running test\n");
+        return;
+    }
+    int loc = glGetUniformLocation(pgm, "u_tex0");
+    if (loc >= 0) glUniform1i(loc, 0);
+    loc = glGetUniformLocation(pgm, "u_tex1");
+    if (loc >= 0) glUniform1i(loc, 1);
+
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glActiveTexture(GL_TEXTURE0);
+
+    glBlendFunc(GL_ONE, GL_ONE);
+    glDisable(GL_BLEND);
+    //sprintf(str2, "%i, %i, %i, %i, %i, 0",
+            //useVarColor, texCount, modulateFirstTex, extraMath, tex0);
+    //doLoop(true, pgm, w, h, str2);
+    //doLoop(false, pgm, w, h, str2);
+
+    glEnable(GL_BLEND);
+    sprintf(gCurrentTestName, "%s, %i, %i, 1", gFragmentTests[pgmNum]->name, pgmNum, tex);
+    doLoop(true, pgm, 100);
+    doLoop(false, pgm, 100);
+}
+
diff --git a/opengl/tests/gl_perf/filltest.cpp b/opengl/tests/gl_perf/filltest.cpp
new file mode 100644
index 0000000..3f8faca
--- /dev/null
+++ b/opengl/tests/gl_perf/filltest.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2007 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 <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <string.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <utils/Timers.h>
+#include <EGL/egl.h>
+#include <utils/Log.h>
+
+
+using namespace android;
+
+
+#include "fill_common.cpp"
+
+
+bool doTest(uint32_t w, uint32_t h) {
+    gWidth = w;
+    gHeight = h;
+    setupVA();
+    genTextures();
+
+    printf("\nvarColor, texCount, modulate, extraMath, texSize, blend, Mpps, DC60\n");
+
+    for (uint32_t num = 0; num < gFragmentTestCount; num++) {
+        doSingleTest(num, 2);
+        if (gFragmentTests[num]->texCount) {
+            doSingleTest(num, 1);
+        }
+    }
+
+    exit(0);
+    return true;
+}
diff --git a/opengl/tests/gl_perf/fragment_shaders.cpp b/opengl/tests/gl_perf/fragment_shaders.cpp
new file mode 100644
index 0000000..79d5ead
--- /dev/null
+++ b/opengl/tests/gl_perf/fragment_shaders.cpp
@@ -0,0 +1,139 @@
+
+typedef struct FragmentTestRec {
+	const char * name;
+	uint32_t texCount;
+	const char * txt;
+} FragmentTest;
+
+static FragmentTest fpFill = {
+	"Solid color", 0,
+
+    "precision mediump float;\n"
+    "uniform vec4 u_color;\n"
+    "void main() {\n"
+    "  gl_FragColor = u_color;\n"
+    "}\n"
+};
+
+static FragmentTest fpGradient = {
+	"Solid gradient", 0,
+
+    "precision mediump float;\n"
+    "varying lowp vec4 v_color;\n"
+    "void main() {\n"
+    "  gl_FragColor = v_color;\n"
+    "}\n"
+};
+
+static FragmentTest fpCopyTex = {
+	"Texture copy", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "uniform sampler2D u_tex0;\n"
+    "void main() {\n"
+    "  gl_FragColor = texture2D(u_tex0, v_tex0);\n"
+    "}\n"
+};
+
+static FragmentTest fpCopyTexGamma = {
+	"Texture copy with gamma", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "uniform sampler2D u_tex0;\n"
+    "void main() {\n"
+    "  vec4 t = texture2D(u_tex0, v_tex0);\n"
+    "  t.rgb = pow(t.rgb, vec3(1.4, 1.4, 1.4));\n"
+    "  gl_FragColor = t;\n"
+    "}\n"
+};
+
+static FragmentTest fpTexSpec = {
+	"Texture spec", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "uniform sampler2D u_tex0;\n"
+    "void main() {\n"
+    "  vec4 t = texture2D(u_tex0, v_tex0);\n"
+    "  float simSpec = dot(gl_FragCoord.xyz, gl_FragCoord.xyz);\n"
+    "  simSpec = pow(clamp(simSpec, 0.1, 1.0), 40.0);\n"
+    "  gl_FragColor = t + vec4(simSpec, simSpec, simSpec, simSpec);\n"
+    "}\n"
+};
+
+static FragmentTest fpDepTex = {
+	"Dependent Lookup", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "uniform sampler2D u_tex0;\n"
+    "void main() {\n"
+    "  vec4 t = texture2D(u_tex0, v_tex0);\n"
+    "  t += texture2D(u_tex0, t.xy);\n"
+    "  gl_FragColor = t;\n"
+    "}\n"
+};
+
+static FragmentTest fpModulateConstantTex = {
+	"Texture modulate constant", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "uniform sampler2D u_tex0;\n"
+    "uniform vec4 u_color;\n"
+
+    "void main() {\n"
+    "  lowp vec4 c = texture2D(u_tex0, v_tex0);\n"
+	"  c *= u_color;\n"
+    "  gl_FragColor = c;\n"
+    "}\n"
+};
+
+static FragmentTest fpModulateVaryingTex = {
+	"Texture modulate gradient", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "varying lowp vec4 v_color;\n"
+    "uniform sampler2D u_tex0;\n"
+
+    "void main() {\n"
+    "  lowp vec4 c = texture2D(u_tex0, v_tex0);\n"
+	"  c *= v_color;\n"
+    "  gl_FragColor = c;\n"
+    "}\n"
+};
+
+static FragmentTest fpModulateVaryingConstantTex = {
+	"Texture modulate gradient constant", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "varying lowp vec4 v_color;\n"
+    "uniform sampler2D u_tex0;\n"
+    "uniform vec4 u_color;\n"
+
+    "void main() {\n"
+    "  lowp vec4 c = texture2D(u_tex0, v_tex0);\n"
+	"  c *= v_color;\n"
+	"  c *= u_color;\n"
+    "  gl_FragColor = c;\n"
+    "}\n"
+};
+
+static FragmentTest *gFragmentTests[] = {
+	&fpFill,
+	&fpGradient,
+	&fpCopyTex,
+	&fpCopyTexGamma,
+   &fpTexSpec,
+   &fpDepTex,
+	&fpModulateConstantTex,
+	&fpModulateVaryingTex,
+	&fpModulateVaryingConstantTex,
+
+};
+
+static const size_t gFragmentTestCount = sizeof(gFragmentTests) / sizeof(gFragmentTests[0]);
diff --git a/opengl/tests/gl_perf/gl2_perf.cpp b/opengl/tests/gl_perf/gl2_perf.cpp
new file mode 100644
index 0000000..9dfcf1c
--- /dev/null
+++ b/opengl/tests/gl_perf/gl2_perf.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2007 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 <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/Timers.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+
+static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
+    if (returnVal != EGL_TRUE) {
+        fprintf(stderr, "%s() returned %d\n", op, returnVal);
+    }
+
+    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
+            = eglGetError()) {
+        fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
+                error);
+    }
+}
+
+static void checkGlError(const char* op) {
+    for (GLint error = glGetError(); error; error
+            = glGetError()) {
+        fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
+    }
+}
+
+bool doTest(uint32_t w, uint32_t h);
+
+static EGLDisplay dpy;
+static EGLSurface surface;
+
+int main(int argc, char** argv) {
+    EGLBoolean returnValue;
+    EGLConfig myConfig = {0};
+
+    EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+    EGLint s_configAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_NONE };
+    EGLint majorVersion;
+    EGLint minorVersion;
+    EGLContext context;
+    EGLint w, h;
+
+
+    checkEglError("<init>");
+    dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    checkEglError("eglGetDisplay");
+    if (dpy == EGL_NO_DISPLAY) {
+        printf("eglGetDisplay returned EGL_NO_DISPLAY.\n");
+        return 0;
+    }
+
+    returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
+    checkEglError("eglInitialize", returnValue);
+    if (returnValue != EGL_TRUE) {
+        printf("eglInitialize failed\n");
+        return 0;
+    }
+
+    EGLNativeWindowType window = android_createDisplaySurface();

+    returnValue = EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &myConfig);
+    if (returnValue) {
+        printf("EGLUtils::selectConfigForNativeWindow() returned %d", returnValue);
+        return 0;
+    }
+
+    checkEglError("EGLUtils::selectConfigForNativeWindow");
+
+    surface = eglCreateWindowSurface(dpy, myConfig, window, NULL);
+    checkEglError("eglCreateWindowSurface");
+    if (surface == EGL_NO_SURFACE) {
+        printf("gelCreateWindowSurface failed.\n");
+        return 0;
+    }
+
+    context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs);
+    checkEglError("eglCreateContext");
+    if (context == EGL_NO_CONTEXT) {
+        printf("eglCreateContext failed\n");
+        return 0;
+    }
+    returnValue = eglMakeCurrent(dpy, surface, surface, context);
+    checkEglError("eglMakeCurrent", returnValue);
+    if (returnValue != EGL_TRUE) {
+        return 0;
+    }
+    eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+    checkEglError("eglQuerySurface");
+    eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+    checkEglError("eglQuerySurface");
+    GLint dim = w < h ? w : h;
+
+    glViewport(0, 0, w, h);
+
+    for (;;) {
+        doTest(w, h);
+        eglSwapBuffers(dpy, surface);
+        checkEglError("eglSwapBuffers");
+    }
+
+    return 0;
+}
+
+void ptSwap() {
+    eglSwapBuffers(dpy, surface);
+}
+
diff --git a/opengl/tests/gl_perfapp/Android.mk b/opengl/tests/gl_perfapp/Android.mk
new file mode 100644
index 0000000..dd75a74
--- /dev/null
+++ b/opengl/tests/gl_perfapp/Android.mk
@@ -0,0 +1,54 @@
+#########################################################################
+# OpenGL ES Perf App
+# This makefile builds both an activity and a shared library.
+#########################################################################
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := GLPerf
+
+LOCAL_JNI_SHARED_LIBRARIES := libglperf
+
+# Run on Eclair
+LOCAL_SDK_VERSION := 7
+
+include $(BUILD_PACKAGE)
+
+#########################################################################
+# Build JNI Shared Library
+#########################################################################
+
+LOCAL_PATH:= $(LOCAL_PATH)/jni
+
+include $(CLEAR_VARS)
+
+# Optional tag would mean it doesn't get installed by default
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -Werror
+
+LOCAL_SRC_FILES:= \
+  gl_code.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libutils \
+	libEGL \
+	libGLESv2
+
+LOCAL_MODULE := libglperf
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif # TARGET_SIMULATOR
diff --git a/opengl/tests/gl_perfapp/AndroidManifest.xml b/opengl/tests/gl_perfapp/AndroidManifest.xml
new file mode 100644
index 0000000..305d95f
--- /dev/null
+++ b/opengl/tests/gl_perfapp/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.glperf"
+    android:versionName="1.0.0" android:versionCode="10000" >
+    <uses-sdk android:targetSdkVersion="7" android:minSdkVersion="7" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application
+            android:label="@string/glperf_activity">
+        <activity android:name="GLPerfActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+            	android:launchMode="singleTask"
+            	android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/gl_perfapp/jni/gl_code.cpp b/opengl/tests/gl_perfapp/jni/gl_code.cpp
new file mode 100644
index 0000000..f993371
--- /dev/null
+++ b/opengl/tests/gl_perfapp/jni/gl_code.cpp
@@ -0,0 +1,103 @@
+// OpenGL ES 2.0 code
+
+#include <nativehelper/jni.h>
+#define LOG_TAG "GLPerf gl_code.cpp"
+#include <utils/Log.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <utils/Timers.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "../../gl_perf/fill_common.cpp"
+
+
+//////////////////////////
+
+// Width and height of the screen
+
+uint32_t w;
+uint32_t h;
+
+// The stateClock starts at zero and increments by 1 every time we draw a frame. It is used to control which phase of the test we are in.
+
+int stateClock;
+const int doLoopStates = 2;
+const int doSingleTestStates = 2;
+bool done;
+
+// Saves the parameters of the test (so we can print them out when we finish the timing.)
+
+
+int pgm;
+
+void ptSwap() {
+}
+
+void doTest() {
+    uint32_t testNum = stateClock >> 2;
+    int texSize = ((stateClock >> 1) & 0x1) + 1;
+
+    if (testNum >= gFragmentTestCount) {
+       LOGI("done\n");
+       if (fOut) {
+           fclose(fOut);
+           fOut = NULL;
+       }
+       done = true;
+       return;
+    }
+
+    // LOGI("doTest %d %d %d\n", texCount, extraMath, testSubState);
+
+//        for (uint32_t num = 0; num < gFragmentTestCount; num++) {
+    doSingleTest(testNum, texSize);
+}
+
+extern "C" {
+    JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_init(JNIEnv * env, jobject obj,  jint width, jint height);
+    JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_step(JNIEnv * env, jobject obj);
+};
+
+JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_init(JNIEnv * env, jobject obj,  jint width, jint height)
+{
+    gWidth = width;
+    gHeight = height;
+    if (!done) {
+            stateClock = 0;
+            done = false;
+            setupVA();
+            genTextures();
+            const char* fileName = "/sdcard/glperf.csv";
+            if (fOut != NULL) {
+                 LOGI("Closing partially written output.n");
+                 fclose(fOut);
+                 fOut = NULL;
+            }
+            LOGI("Writing to: %s\n",fileName);
+            fOut = fopen(fileName, "w");
+            if (fOut == NULL) {
+                LOGE("Could not open: %s\n", fileName);
+            }
+
+            LOGI("\nvarColor, texCount, modulate, extraMath, texSize, blend, Mpps, DC60\n");
+            if (fOut) fprintf(fOut,"varColor, texCount, modulate, extraMath, texSize, blend, Mpps, DC60\r\n");
+    }
+}
+
+JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_step(JNIEnv * env, jobject obj)
+{
+    if (! done) {
+        if (stateClock > 0 && ((stateClock & 1) == 0)) {
+            //endTimer(100);
+        }
+        doTest();
+        stateClock++;
+    } else {
+            glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    }
+}
diff --git a/opengl/tests/gl_perfapp/res/values/strings.xml b/opengl/tests/gl_perfapp/res/values/strings.xml
new file mode 100644
index 0000000..dc21075
--- /dev/null
+++ b/opengl/tests/gl_perfapp/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="glperf_activity">GLPerf</string>
+
+</resources>
+
diff --git a/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfActivity.java b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfActivity.java
new file mode 100644
index 0000000..e3f3abf
--- /dev/null
+++ b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.android.glperf;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.io.File;
+
+
+public class GLPerfActivity extends Activity {
+
+    GLPerfView mView;
+
+    @Override protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+	getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        mView = new GLPerfView(getApplication());
+	setContentView(mView);
+    }
+
+    @Override protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfLib.java b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfLib.java
new file mode 100644
index 0000000..89a0e54
--- /dev/null
+++ b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfLib.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.android.glperf;
+
+// Wrapper for native library
+
+public class GLPerfLib {
+
+     static {
+         System.loadLibrary("glperf");
+     }
+
+    /**
+     * @param width the current view width
+     * @param height the current view height
+     */
+     public static native void init(int width, int height);
+     public static native void step();
+}
diff --git a/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfView.java b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfView.java
new file mode 100644
index 0000000..4ce4a4d
--- /dev/null
+++ b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfView.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package com.android.glperf;
+/*
+ * Copyright (C) 2008 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.
+ */
+
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation.  This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+class GLPerfView extends GLSurfaceView {
+    private static String TAG = "GLPerfView";
+
+    public GLPerfView(Context context) {
+        super(context);
+        init(false, 0, 0);
+    }
+
+    public GLPerfView(Context context, boolean translucent, int depth, int stencil) {
+        super(context);
+        init(translucent, depth, stencil);
+    }
+
+    private void init(boolean translucent, int depth, int stencil) {
+        setEGLContextFactory(new ContextFactory());
+        setEGLConfigChooser( translucent ?
+              new ConfigChooser(8,8,8,8, depth, stencil) :
+              new ConfigChooser(5,6,5,0, depth, stencil));
+        setRenderer(new Renderer());
+    }
+
+    private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
+        private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+        public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
+            Log.w(TAG, "creating OpenGL ES 2.0 context");
+            checkEglError("Before eglCreateContext", egl);
+            int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
+            EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
+            checkEglError("After eglCreateContext", egl);
+            return context;
+        }
+
+        public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
+            egl.eglDestroyContext(display, context);
+        }
+    }
+
+    private static void checkEglError(String prompt, EGL10 egl) {
+        int error;
+        while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
+            Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
+        }
+    }
+
+    private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
+        private static int EGL_OPENGL_ES2_BIT = 4;
+        private static int[] s_configAttribs2 =
+        {
+            EGL10.EGL_RED_SIZE, 4,
+            EGL10.EGL_GREEN_SIZE, 4,
+            EGL10.EGL_BLUE_SIZE, 4,
+            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL10.EGL_NONE
+        };
+
+        public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
+            mRedSize = r;
+            mGreenSize = g;
+            mBlueSize = b;
+            mAlphaSize = a;
+            mDepthSize = depth;
+            mStencilSize = stencil;
+        }
+
+        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+
+            int[] num_config = new int[1];
+            egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);
+
+            int numConfigs = num_config[0];
+
+            if (numConfigs <= 0) {
+                throw new IllegalArgumentException("No configs match configSpec");
+            }
+            EGLConfig[] configs = new EGLConfig[numConfigs];
+            egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
+            // printConfigs(egl, display, configs);
+            return chooseConfig(egl, display, configs);
+        }
+
+        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+                EGLConfig[] configs) {
+            EGLConfig closestConfig = null;
+            int closestDistance = 1000;
+            for(EGLConfig config : configs) {
+                int d = findConfigAttrib(egl, display, config,
+                        EGL10.EGL_DEPTH_SIZE, 0);
+                int s = findConfigAttrib(egl, display, config,
+                        EGL10.EGL_STENCIL_SIZE, 0);
+                if (d >= mDepthSize && s>= mStencilSize) {
+                    int r = findConfigAttrib(egl, display, config,
+                            EGL10.EGL_RED_SIZE, 0);
+                    int g = findConfigAttrib(egl, display, config,
+                             EGL10.EGL_GREEN_SIZE, 0);
+                    int b = findConfigAttrib(egl, display, config,
+                              EGL10.EGL_BLUE_SIZE, 0);
+                    int a = findConfigAttrib(egl, display, config,
+                            EGL10.EGL_ALPHA_SIZE, 0);
+                    int distance = Math.abs(r - mRedSize)
+                                + Math.abs(g - mGreenSize)
+                                + Math.abs(b - mBlueSize)
+                                + Math.abs(a - mAlphaSize);
+                    if (distance < closestDistance) {
+                        closestDistance = distance;
+                        closestConfig = config;
+                    }
+                }
+            }
+            return closestConfig;
+        }
+
+        private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+                EGLConfig config, int attribute, int defaultValue) {
+
+            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+                return mValue[0];
+            }
+            return defaultValue;
+        }
+
+        private void printConfigs(EGL10 egl, EGLDisplay display,
+            EGLConfig[] configs) {
+            int numConfigs = configs.length;
+            Log.w(TAG, String.format("%d configurations", numConfigs));
+            for (int i = 0; i < numConfigs; i++) {
+                Log.w(TAG, String.format("Configuration %d:\n", i));
+                printConfig(egl, display, configs[i]);
+            }
+        }
+
+        private void printConfig(EGL10 egl, EGLDisplay display,
+                EGLConfig config) {
+            int[] attributes = {
+                    EGL10.EGL_BUFFER_SIZE,
+                    EGL10.EGL_ALPHA_SIZE,
+                    EGL10.EGL_BLUE_SIZE,
+                    EGL10.EGL_GREEN_SIZE,
+                    EGL10.EGL_RED_SIZE,
+                    EGL10.EGL_DEPTH_SIZE,
+                    EGL10.EGL_STENCIL_SIZE,
+                    EGL10.EGL_CONFIG_CAVEAT,
+                    EGL10.EGL_CONFIG_ID,
+                    EGL10.EGL_LEVEL,
+                    EGL10.EGL_MAX_PBUFFER_HEIGHT,
+                    EGL10.EGL_MAX_PBUFFER_PIXELS,
+                    EGL10.EGL_MAX_PBUFFER_WIDTH,
+                    EGL10.EGL_NATIVE_RENDERABLE,
+                    EGL10.EGL_NATIVE_VISUAL_ID,
+                    EGL10.EGL_NATIVE_VISUAL_TYPE,
+                    0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+                    EGL10.EGL_SAMPLES,
+                    EGL10.EGL_SAMPLE_BUFFERS,
+                    EGL10.EGL_SURFACE_TYPE,
+                    EGL10.EGL_TRANSPARENT_TYPE,
+                    EGL10.EGL_TRANSPARENT_RED_VALUE,
+                    EGL10.EGL_TRANSPARENT_GREEN_VALUE,
+                    EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+                    0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
+                    0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+                    0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
+                    0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
+                    EGL10.EGL_LUMINANCE_SIZE,
+                    EGL10.EGL_ALPHA_MASK_SIZE,
+                    EGL10.EGL_COLOR_BUFFER_TYPE,
+                    EGL10.EGL_RENDERABLE_TYPE,
+                    0x3042 // EGL10.EGL_CONFORMANT
+            };
+            String[] names = {
+                    "EGL_BUFFER_SIZE",
+                    "EGL_ALPHA_SIZE",
+                    "EGL_BLUE_SIZE",
+                    "EGL_GREEN_SIZE",
+                    "EGL_RED_SIZE",
+                    "EGL_DEPTH_SIZE",
+                    "EGL_STENCIL_SIZE",
+                    "EGL_CONFIG_CAVEAT",
+                    "EGL_CONFIG_ID",
+                    "EGL_LEVEL",
+                    "EGL_MAX_PBUFFER_HEIGHT",
+                    "EGL_MAX_PBUFFER_PIXELS",
+                    "EGL_MAX_PBUFFER_WIDTH",
+                    "EGL_NATIVE_RENDERABLE",
+                    "EGL_NATIVE_VISUAL_ID",
+                    "EGL_NATIVE_VISUAL_TYPE",
+                    "EGL_PRESERVED_RESOURCES",
+                    "EGL_SAMPLES",
+                    "EGL_SAMPLE_BUFFERS",
+                    "EGL_SURFACE_TYPE",
+                    "EGL_TRANSPARENT_TYPE",
+                    "EGL_TRANSPARENT_RED_VALUE",
+                    "EGL_TRANSPARENT_GREEN_VALUE",
+                    "EGL_TRANSPARENT_BLUE_VALUE",
+                    "EGL_BIND_TO_TEXTURE_RGB",
+                    "EGL_BIND_TO_TEXTURE_RGBA",
+                    "EGL_MIN_SWAP_INTERVAL",
+                    "EGL_MAX_SWAP_INTERVAL",
+                    "EGL_LUMINANCE_SIZE",
+                    "EGL_ALPHA_MASK_SIZE",
+                    "EGL_COLOR_BUFFER_TYPE",
+                    "EGL_RENDERABLE_TYPE",
+                    "EGL_CONFORMANT"
+            };
+            int[] value = new int[1];
+            for (int i = 0; i < attributes.length; i++) {
+                int attribute = attributes[i];
+                String name = names[i];
+                if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
+                    Log.w(TAG, String.format("  %s: %d\n", name, value[0]));
+                } else {
+                    // Log.w(TAG, String.format("  %s: failed\n", name));
+                    while (egl.eglGetError() != EGL10.EGL_SUCCESS);
+                }
+            }
+        }
+
+        // Subclasses can adjust these values:
+        protected int mRedSize;
+        protected int mGreenSize;
+        protected int mBlueSize;
+        protected int mAlphaSize;
+        protected int mDepthSize;
+        protected int mStencilSize;
+        private int[] mValue = new int[1];
+    }
+
+    private static class Renderer implements GLSurfaceView.Renderer {
+        public void onDrawFrame(GL10 gl) {
+            GLPerfLib.step();
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            GLPerfLib.init(width, height);
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+            // Do nothing.
+        }
+    }
+}
+
diff --git a/opengl/tests/testFramerate/Android.mk b/opengl/tests/testFramerate/Android.mk
new file mode 100644
index 0000000..500abf3
--- /dev/null
+++ b/opengl/tests/testFramerate/Android.mk
@@ -0,0 +1,19 @@
+#########################################################################
+# Test framerate and look for hiccups
+#########################################################################
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TestFramerate
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testFramerate/AndroidManifest.xml b/opengl/tests/testFramerate/AndroidManifest.xml
new file mode 100644
index 0000000..e04342c
--- /dev/null
+++ b/opengl/tests/testFramerate/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.testframerate">
+    <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+
+    <application
+            android:label="@string/testFramerate_activity">
+        <activity android:name="TestFramerateActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+                android:launchMode="singleTask"
+                android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/testFramerate/res/values/strings.xml b/opengl/tests/testFramerate/res/values/strings.xml
new file mode 100644
index 0000000..e6b3088
--- /dev/null
+++ b/opengl/tests/testFramerate/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="testFramerate_activity">TestFramerate</string>
+
+</resources>
+
diff --git a/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java
new file mode 100644
index 0000000..cbe279b
--- /dev/null
+++ b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.android.testframerate;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.io.File;
+
+
+public class TestFramerateActivity extends Activity {
+
+    TestFramerateView mView;
+
+    @Override protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mView = new TestFramerateView(getApplication());
+        setContentView(mView);
+        mView.setFocusableInTouchMode(true);
+    }
+
+    @Override protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java
new file mode 100644
index 0000000..f3fb5de
--- /dev/null
+++ b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package com.android.testframerate;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLES20;
+
+class TestFramerateView extends GLSurfaceView {
+    private static String TAG = "TestFramerateView";
+
+    public TestFramerateView(Context context) {
+        super(context);
+        setEGLContextClientVersion(2);
+        setRenderer(new Renderer());
+    }
+
+    private long mLastTime_us = 0;
+    private long mNumShortFramesElapsed = 0;
+    private void registerTime(long now_us) {
+        long longFrameTime_ms = Integer.parseInt(SystemProperties.get("debug.longframe_ms", "16"));
+        long elapsedTime_us = now_us - mLastTime_us;
+        float fps = 1000000.f / elapsedTime_us;
+        if (mLastTime_us > 0 && elapsedTime_us > longFrameTime_ms*1000) {
+          Log.v(TAG, "Long frame: " + elapsedTime_us/1000.f + " ms (" + fps + " fps)");
+          if (mNumShortFramesElapsed > 0) {
+            Log.v(TAG, "  Short frames since last long frame: " + mNumShortFramesElapsed);
+            mNumShortFramesElapsed = 0;
+          }
+        } else {
+            ++mNumShortFramesElapsed;
+        }
+
+        mLastTime_us = now_us;
+    }
+
+    private class Renderer implements GLSurfaceView.Renderer {
+        public Renderer() {
+        }
+
+
+        public void onDrawFrame(GL10 gl) {
+            long now_us = System.nanoTime() / 1000;
+            registerTime(now_us);
+
+            float red = (now_us % 1000000) / 1000000.f;
+            float green = (now_us % 2000000) / 2000000.f;
+            float blue = (now_us % 3000000) / 3000000.f;
+            GLES20.glClearColor(red, green, blue, 1.0f);
+            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            GLES20.glViewport(0, 0, width, height);
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        }
+
+    }
+}
diff --git a/opengl/tests/testLatency/Android.mk b/opengl/tests/testLatency/Android.mk
new file mode 100644
index 0000000..96417c7
--- /dev/null
+++ b/opengl/tests/testLatency/Android.mk
@@ -0,0 +1,20 @@
+#########################################################################
+# Test end-to-end latency.
+#########################################################################
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SDK_VERSION := 8
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TestLatency
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testLatency/AndroidManifest.xml b/opengl/tests/testLatency/AndroidManifest.xml
new file mode 100644
index 0000000..741266e
--- /dev/null
+++ b/opengl/tests/testLatency/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.testlatency">
+    <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+
+    <application
+            android:label="@string/testLatency_activity">
+        <activity android:name="TestLatencyActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+                android:launchMode="singleTask"
+                android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/testLatency/res/values/strings.xml b/opengl/tests/testLatency/res/values/strings.xml
new file mode 100644
index 0000000..0309991
--- /dev/null
+++ b/opengl/tests/testLatency/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="testLatency_activity">TestLatency</string>
+
+</resources>
+
diff --git a/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyActivity.java b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyActivity.java
new file mode 100644
index 0000000..ed993cb
--- /dev/null
+++ b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.android.testlatency;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.io.File;
+
+
+public class TestLatencyActivity extends Activity {
+
+    TestLatencyView mView;
+
+    @Override protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mView = new TestLatencyView(getApplication());
+        setContentView(mView);
+        mView.setFocusableInTouchMode(true);
+    }
+
+    @Override protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyView.java b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyView.java
new file mode 100644
index 0000000..d62bf17
--- /dev/null
+++ b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyView.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package com.android.testlatency;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLES20;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation.  This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+class TestLatencyView extends GLSurfaceView {
+    private static String TAG = "TestLatencyiew";
+    private float mX;
+    private float mY;
+    private float mDX;
+    private float mDY;
+    private long  mT;
+    private long  mDT;
+
+    public TestLatencyView(Context context) {
+        super(context);
+        setEGLContextClientVersion(2);
+        setRenderer(new Renderer());
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getAction()) {
+        case MotionEvent.ACTION_MOVE:
+            float x = event.getX();
+            float y = event.getY();
+            long  t = event.getEventTime();
+            synchronized(this) {
+                mDT = t - mT;
+                mT = t;
+                mDX = x - mX;
+                mX = x;
+                mDY = y - mY;
+                mY = y;
+            }
+            break;
+        default:
+            break;
+        }
+        return true;
+    }
+
+    private class Renderer implements GLSurfaceView.Renderer {
+        private float mScaleX, mScaleY, mOffsetX, mOffsetY;
+        private final float MS_PER_FRAME = 1000 / 60;
+        public Renderer() {
+            mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length * 4)
+                .order(ByteOrder.nativeOrder()).asFloatBuffer();
+        }
+
+
+        public void onDrawFrame(GL10 gl) {
+            GLES20.glClearColor(0.4f, 0.4f, 0.4f, 1.0f);
+            GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+            GLES20.glUseProgram(mProgram);
+            checkGlError("glUseProgram");
+
+            float x, y, dx, dy;
+            long t, dt;
+            synchronized(TestLatencyView.this) {
+                x = mX;
+                y = mY;
+                dx = mDX;
+                dy = mDY;
+                dt = mDT;
+            }
+
+            if (dt > 0) {
+                dx = dx * MS_PER_FRAME / dt;
+                dy = dy * MS_PER_FRAME / dt;
+            }
+
+            GLES20.glEnableVertexAttribArray(mvPositionHandle);
+            checkGlError("glEnableVertexAttribArray");
+            GLES20.glEnableVertexAttribArray(mvColorHandle);
+            checkGlError("glEnableVertexAttribArray");
+            for(int step = 0; step < 8; step++) {
+                float sx = (x + dx * step) * mScaleX + mOffsetX;
+                float sy = (y + dy * step) * mScaleY + mOffsetY;
+                int cbase = step * 4;
+
+                for (int i = 0; i < mTriangleVerticesData.length; i += 6) {
+                    mTriangleVerticesData2[i] = sx + mTriangleVerticesData[i];
+                    mTriangleVerticesData2[i+1] = -sy + mTriangleVerticesData[i+1];
+                    mTriangleVerticesData2[i+2] = mColors[cbase];
+                    mTriangleVerticesData2[i+3] = mColors[cbase+1];
+                    mTriangleVerticesData2[i+4] = mColors[cbase+2];
+                    mTriangleVerticesData2[i+5] = mColors[cbase+3];
+                }
+                mTriangleVertices.position(0);
+                mTriangleVertices.put(mTriangleVerticesData2).position(0);
+
+                GLES20.glVertexAttribPointer(mvPositionHandle, 2, GLES20.GL_FLOAT, false, 6*4, mTriangleVertices);
+                checkGlError("glVertexAttribPointer mvPosition");
+                mTriangleVertices.put(mTriangleVerticesData2).position(2);
+                GLES20.glVertexAttribPointer(mvColorHandle, 4, GLES20.GL_FLOAT, false, 6*4, mTriangleVertices);
+                checkGlError("glVertexAttribPointer mvColor");
+                GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
+                checkGlError("glDrawArrays");
+            }
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            GLES20.glViewport(0, 0, width, height);
+            mScaleX = 2.0f / width;
+            mScaleY = 2.0f / height;
+            mOffsetX = -1f;
+            mOffsetY = -1f;
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+            mProgram = createProgram(mVertexShader, mFragmentShader);
+            if (mProgram == 0) {
+                return;
+            }
+            mvPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
+            checkGlError("glGetAttribLocation");
+            if (mvPositionHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for vPosition");
+            }
+            mvColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
+            checkGlError("glGetAttribLocation");
+            if (mvColorHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for vColor");
+            }
+        }
+
+        private int loadShader(int shaderType, String source) {
+            int shader = GLES20.glCreateShader(shaderType);
+            if (shader != 0) {
+                GLES20.glShaderSource(shader, source);
+                GLES20.glCompileShader(shader);
+                int[] compiled = new int[1];
+                GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+                if (compiled[0] == 0) {
+                    Log.e(TAG, "Could not compile shader " + shaderType + ":");
+                    Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
+                    GLES20.glDeleteShader(shader);
+                    shader = 0;
+                }
+            }
+            return shader;
+        }
+
+        private int createProgram(String vertexSource, String fragmentSource) {
+            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+            if (vertexShader == 0) {
+                return 0;
+            }
+
+            int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+            if (pixelShader == 0) {
+                return 0;
+            }
+
+            int program = GLES20.glCreateProgram();
+            if (program != 0) {
+                GLES20.glAttachShader(program, vertexShader);
+                checkGlError("glAttachShader vertexShader");
+                GLES20.glAttachShader(program, pixelShader);
+                checkGlError("glAttachShader pixelShader");
+                GLES20.glLinkProgram(program);
+                int[] linkStatus = new int[1];
+                GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+                if (linkStatus[0] != GLES20.GL_TRUE) {
+                    Log.e(TAG, "Could not link program: ");
+                    Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+                    GLES20.glDeleteProgram(program);
+                    program = 0;
+                }
+            }
+            return program;
+        }
+
+        private void checkGlError(String op) {
+            int error;
+            while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+                Log.e(TAG, op + ": glError " + error);
+                throw new RuntimeException(op + ": glError " + error);
+            }
+        }
+
+        // X, Y, R G B A
+        private final float[] mTriangleVerticesData = {
+                -0.025f, 0.3f, 0.0f, 1.0f, 0.0f, 1.0f,
+                 0.0f  , 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+                 0.025f, 0.3f, 1.0f, 1.0f, 255.0f, 1.0f
+                };
+
+        // Color cascade:
+        private final float[] mColors = {
+                0.0f, 0.0f, 0.0f, 1.0f,
+                0.5f, 0.0f, 0.0f, 1.0f,
+                0.0f, 0.5f, 0.0f, 1.0f,
+                0.5f, 0.5f, 0.0f, 1.0f,
+
+                0.0f, 0.0f, 0.5f, 1.0f,
+                1.0f, 0.0f, 0.0f, 1.0f,
+                1.0f, 1.0f, 1.0f, 1.0f,
+                0.0f, 1.0f, 0.0f, 1.0f
+        };
+
+        private float[] mTriangleVerticesData2 = new float[mTriangleVerticesData.length];
+        private FloatBuffer mTriangleVertices;
+
+        private final String mVertexShader = "attribute vec4 aPosition;\n"
+            + "attribute vec4 aColor;\n"
+            + "varying vec4 vColor;\n"
+            + "void main() {\n"
+            + "  gl_Position = aPosition;\n"
+            + "  vColor = aColor;\n"
+            + "}\n";
+
+        private final String mFragmentShader = "precision mediump float;\n"
+            + "varying vec4 vColor;\n"
+            + "void main() {\n"
+            + "  gl_FragColor = vColor;\n"
+            + "}\n";
+
+        private int mProgram;
+        private int mvPositionHandle;
+        private int mvColorHandle;
+
+    }
+}
+
diff --git a/opengl/tests/testViewport/Android.mk b/opengl/tests/testViewport/Android.mk
new file mode 100644
index 0000000..ab37809
--- /dev/null
+++ b/opengl/tests/testViewport/Android.mk
@@ -0,0 +1,26 @@
+#########################################################################
+# OpenGL ES JNI sample
+# This makefile builds both an activity and a shared library.
+#########################################################################
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TestViewport
+
+# Set a specific SDK version so we can run on Froyo.
+
+LOCAL_SDK_VERSION := 8
+
+include $(BUILD_PACKAGE)
+
+endif # TARGET_SIMULATOR
diff --git a/opengl/tests/testViewport/AndroidManifest.xml b/opengl/tests/testViewport/AndroidManifest.xml
new file mode 100644
index 0000000..90a9d2d
--- /dev/null
+++ b/opengl/tests/testViewport/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test">
+    <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application
+            android:label="@string/test_activity">
+        <activity android:name="TestActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+            	android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/testViewport/README b/opengl/tests/testViewport/README
new file mode 100644
index 0000000..c06abc9
--- /dev/null
+++ b/opengl/tests/testViewport/README
@@ -0,0 +1,28 @@
+Repro steps:
+
+build, install and run the attached test program TestViewport.apk
+
+Run on Sapphire with Froyo.
+
+The program clears the screen to blue, then draws a full screen white quad that
+is alligned to the screen.
+(Therefore the whole screen should appear to be white.)
+
+
+Note that screen is all white.
+
+Rotate screen 90 degrees.
+
+Expected: screen is still all white.
+
+Actual: screen is blue with offset white rectangle.
+
+This bug only happens on Sapphire, it works correctly on Passion.
+
+What happens:
+
+I think the bug is that the gl.glViewport() call in onSurfaceChanged() is
+being ignored by the OpenGL driver.
+
+NOTE: If a gl.glViewport call is added at the beginning of the onDrawFrame()
+call (which means it is called before every draw), the program runs correctly.
diff --git a/opengl/tests/testViewport/res/values/strings.xml b/opengl/tests/testViewport/res/values/strings.xml
new file mode 100644
index 0000000..f4b8bbb
--- /dev/null
+++ b/opengl/tests/testViewport/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="test_activity">Test Viewport</string>
+
+</resources>
+
diff --git a/opengl/tests/testViewport/src/com/android/test/TestActivity.java b/opengl/tests/testViewport/src/com/android/test/TestActivity.java
new file mode 100644
index 0000000..cc7e450
--- /dev/null
+++ b/opengl/tests/testViewport/src/com/android/test/TestActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+package com.android.test;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivity extends Activity {
+    private final static String TAG = "TestActivity";
+    TestView mView;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mView = new TestView(getApplication());
+	    mView.setFocusableInTouchMode(true);
+	    setContentView(mView);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/testViewport/src/com/android/test/TestView.java b/opengl/tests/testViewport/src/com/android/test/TestView.java
new file mode 100644
index 0000000..23cc37d
--- /dev/null
+++ b/opengl/tests/testViewport/src/com/android/test/TestView.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package com.android.test;
+/*
+ * Copyright (C) 2008 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.
+ */
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.FloatBuffer;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation.  This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+class TestView extends GLSurfaceView {
+    TestView(Context context) {
+        super(context);
+        init();
+    }
+
+    public TestView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    private void init() {
+        setRenderer(new Renderer());
+        setRenderMode(RENDERMODE_WHEN_DIRTY);
+    }
+    
+        /** A grid is a topologically rectangular array of vertices.
+    *
+    * The vertex and index data are held in VBO objects because on most
+    * GPUs VBO objects are the fastest way of rendering static vertex
+    * and index data.
+    *
+    */
+
+   private static class Grid {
+       // Size of vertex data elements in bytes:
+       final static int FLOAT_SIZE = 4;
+       final static int CHAR_SIZE = 2;
+
+       // Vertex structure:
+       // float x, y, z;
+
+       final static int VERTEX_SIZE = 3 * FLOAT_SIZE;
+
+       private int mVertexBufferObjectId;
+       private int mElementBufferObjectId;
+
+       // These buffers are used to hold the vertex and index data while
+       // constructing the grid. Once createBufferObjects() is called
+       // the buffers are nulled out to save memory.
+
+       private ByteBuffer mVertexByteBuffer;
+       private FloatBuffer mVertexBuffer;
+       private CharBuffer mIndexBuffer;
+
+       private int mW;
+       private int mH;
+       private int mIndexCount;
+
+       public Grid(int w, int h) {
+           if (w < 0 || w >= 65536) {
+               throw new IllegalArgumentException("w");
+           }
+           if (h < 0 || h >= 65536) {
+               throw new IllegalArgumentException("h");
+           }
+           if (w * h >= 65536) {
+               throw new IllegalArgumentException("w * h >= 65536");
+           }
+
+           mW = w;
+           mH = h;
+           int size = w * h;
+
+           mVertexByteBuffer = ByteBuffer.allocateDirect(VERTEX_SIZE * size)
+               .order(ByteOrder.nativeOrder());
+           mVertexBuffer = mVertexByteBuffer.asFloatBuffer();
+
+           int quadW = mW - 1;
+           int quadH = mH - 1;
+           int quadCount = quadW * quadH;
+           int indexCount = quadCount * 6;
+           mIndexCount = indexCount;
+           mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount)
+               .order(ByteOrder.nativeOrder()).asCharBuffer();
+
+           /*
+            * Initialize triangle list mesh.
+            *
+            *     [0]-----[  1] ...
+            *      |    /   |
+            *      |   /    |
+            *      |  /     |
+            *     [w]-----[w+1] ...
+            *      |       |
+            *
+            */
+
+           {
+               int i = 0;
+               for (int y = 0; y < quadH; y++) {
+                   for (int x = 0; x < quadW; x++) {
+                       char a = (char) (y * mW + x);
+                       char b = (char) (y * mW + x + 1);
+                       char c = (char) ((y + 1) * mW + x);
+                       char d = (char) ((y + 1) * mW + x + 1);
+
+                       mIndexBuffer.put(i++, a);
+                       mIndexBuffer.put(i++, c);
+                       mIndexBuffer.put(i++, b);
+
+                       mIndexBuffer.put(i++, b);
+                       mIndexBuffer.put(i++, c);
+                       mIndexBuffer.put(i++, d);
+                   }
+               }
+           }
+
+       }
+
+       public void set(int i, int j, float x, float y, float z) {
+           if (i < 0 || i >= mW) {
+               throw new IllegalArgumentException("i");
+           }
+           if (j < 0 || j >= mH) {
+               throw new IllegalArgumentException("j");
+           }
+
+           int index = mW * j + i;
+
+           mVertexBuffer.position(index * VERTEX_SIZE / FLOAT_SIZE);
+           mVertexBuffer.put(x);
+           mVertexBuffer.put(y);
+           mVertexBuffer.put(z);
+       }
+
+       public void createBufferObjects(GL gl) {
+           // Generate a the vertex and element buffer IDs
+           int[] vboIds = new int[2];
+           GL11 gl11 = (GL11) gl;
+           gl11.glGenBuffers(2, vboIds, 0);
+           mVertexBufferObjectId = vboIds[0];
+           mElementBufferObjectId = vboIds[1];
+
+           // Upload the vertex data
+           gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId);
+           mVertexByteBuffer.position(0);
+           gl11.glBufferData(GL11.GL_ARRAY_BUFFER, mVertexByteBuffer.capacity(), mVertexByteBuffer, GL11.GL_STATIC_DRAW);
+
+           gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId);
+           mIndexBuffer.position(0);
+           gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer.capacity() * CHAR_SIZE, mIndexBuffer, GL11.GL_STATIC_DRAW);
+
+           // We don't need the in-memory data any more
+           mVertexBuffer = null;
+           mVertexByteBuffer = null;
+           mIndexBuffer = null;
+       }
+
+       public void draw(GL10 gl) {
+           GL11 gl11 = (GL11) gl;
+
+           gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+
+           gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId);
+           gl11.glVertexPointer(3, GL10.GL_FLOAT, VERTEX_SIZE, 0);
+           
+           gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId);
+           gl11.glDrawElements(GL10.GL_TRIANGLES, mIndexCount, GL10.GL_UNSIGNED_SHORT, 0);
+           gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
+           gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
+           gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
+       }
+   }
+
+
+    private class Renderer implements GLSurfaceView.Renderer {
+        private static final String TAG = "Renderer";
+        private Grid mGrid;
+        
+        public void onDrawFrame(GL10 gl) {
+			gl.glClearColor(0,0,1,1);
+			gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
+            mGrid.draw(gl);
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            gl.glViewport(0, 0, width, height);
+			gl.glMatrixMode(GL11.GL_PROJECTION);
+			gl.glLoadIdentity();
+			gl.glOrthof(0, width, height, 0, -1, 1);
+			gl.glMatrixMode(GL11.GL_MODELVIEW);
+            createGrid(gl, width, height);
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        }
+        
+        private void createGrid(GL10 gl, float w, float h) {
+        mGrid = new Grid(2, 2);
+			for (int j = 0; j < 2; j++) {
+				for (int i = 0; i < 2; i++) {
+					float x = w * i;
+					float y = h * j;
+					float z = 0.0f;
+					mGrid.set(i,j, x, y, z);
+				}
+			}
+			mGrid.createBufferObjects(gl);
+		}
+    }
+}
+
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index a14bfb5..e4825d0 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -5,6 +5,7 @@
     clz.cpp.arm \
     DisplayHardware/DisplayHardware.cpp \
     DisplayHardware/DisplayHardwareBase.cpp \
+    DisplayHardware/HWComposer.cpp \
     BlurFilter.cpp.arm \
     GLExtensions.cpp \
     Layer.cpp \
@@ -21,7 +22,7 @@
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 
 ifeq ($(TARGET_BOARD_PLATFORM), omap3)
-	LOCAL_CFLAGS += -DNO_RGBX_8888
+	LOCAL_CFLAGS += -DNO_RGBX_8888 -DHAS_PUSH_BUFFERS
 endif
 
 # need "-lrt" on Linux simulator to pick up clock_gettime
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 0515110..bd348bf 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -36,11 +36,11 @@
 
 #include "DisplayHardware/DisplayHardware.h"
 
-#include <hardware/copybit.h>
 #include <hardware/overlay.h>
 #include <hardware/gralloc.h>
 
 #include "GLExtensions.h"
+#include "HWComposer.h"
 
 using namespace android;
 
@@ -76,7 +76,7 @@
         const sp<SurfaceFlinger>& flinger,
         uint32_t dpy)
     : DisplayHardwareBase(flinger, dpy),
-      mFlags(0)
+      mFlags(0), mHwc(0)
 {
     init(dpy);
 }
@@ -262,6 +262,17 @@
 
     // Unbind the context from this thread
     eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+
+    // initialize the H/W composer
+    mHwc = new HWComposer();
+    if (mHwc->initCheck() == NO_ERROR) {
+        mHwc->setFrameBuffer(mDisplay, mSurface);
+    }
+}
+
+HWComposer& DisplayHardware::getHwComposer() const {
+    return *mHwc;
 }
 
 /*
@@ -281,6 +292,9 @@
 void DisplayHardware::releaseScreen() const
 {
     DisplayHardwareBase::releaseScreen();
+    if (mHwc->initCheck() == NO_ERROR) {
+        mHwc->release();
+    }
 }
 
 void DisplayHardware::acquireScreen() const
@@ -321,7 +335,12 @@
     }
     
     mPageFlipCount++;
-    eglSwapBuffers(dpy, surface);
+
+    if (mHwc->initCheck() == NO_ERROR) {
+        mHwc->commit();
+    } else {
+        eglSwapBuffers(dpy, surface);
+    }
     checkEGLErrors("eglSwapBuffers");
 
     // for debugging
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index 2d7900c..75b55df 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -34,12 +34,11 @@
 #include "DisplayHardware/DisplayHardwareBase.h"
 
 struct overlay_control_device_t;
-struct framebuffer_device_t;
-struct copybit_image_t;
 
 namespace android {
 
 class FramebufferNativeWindow;
+class HWComposer;
 
 class DisplayHardware : public DisplayHardwareBase
 {
@@ -80,6 +79,9 @@
     uint32_t getPageFlipCount() const;
     EGLDisplay getEGLDisplay() const { return mDisplay; }
     overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
+
+    // Hardware Composer
+    HWComposer& getHwComposer() const;
     
     status_t compositionComplete() const;
     
@@ -110,6 +112,8 @@
     GLint           mMaxViewportDims;
     GLint           mMaxTextureSize;
     
+    HWComposer*     mHwc;
+
     sp<FramebufferNativeWindow> mNativeWindow;
     overlay_control_device_t* mOverlayEngine;
 };
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
new file mode 100644
index 0000000..4af274b
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+#include <hardware/hardware.h>
+
+#include <cutils/log.h>
+
+#include <EGL/egl.h>
+
+#include "HWComposer.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+HWComposer::HWComposer()
+    : mModule(0), mHwc(0), mList(0), mCapacity(0),
+      mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE)
+{
+    int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
+    LOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
+    if (err == 0) {
+        err = hwc_open(mModule, &mHwc);
+        LOGE_IF(err, "%s device failed to initialize (%s)",
+                HWC_HARDWARE_COMPOSER, strerror(-err));
+    }
+}
+
+HWComposer::~HWComposer() {
+    free(mList);
+    if (mHwc) {
+        hwc_close(mHwc);
+    }
+}
+
+status_t HWComposer::initCheck() const {
+    return mHwc ? NO_ERROR : NO_INIT;
+}
+
+void HWComposer::setFrameBuffer(EGLDisplay dpy, EGLSurface sur) {
+    mDpy = (hwc_display_t)dpy;
+    mSur = (hwc_surface_t)sur;
+}
+
+status_t HWComposer::createWorkList(size_t numLayers) {
+    if (mHwc) {
+        if (!mList || mCapacity < numLayers) {
+            free(mList);
+            size_t size = sizeof(hwc_layer_list) + numLayers*sizeof(hwc_layer_t);
+            mList = (hwc_layer_list_t*)malloc(size);
+            mCapacity = numLayers;
+        }
+        mList->flags = HWC_GEOMETRY_CHANGED;
+        mList->numHwLayers = numLayers;
+    }
+    return NO_ERROR;
+}
+
+status_t HWComposer::prepare() const {
+    int err = mHwc->prepare(mHwc, mList);
+    return (status_t)err;
+}
+
+status_t HWComposer::commit() const {
+    int err = mHwc->set(mHwc, mDpy, mSur, mList);
+    if (mList) {
+        mList->flags &= ~HWC_GEOMETRY_CHANGED;
+    }
+    return (status_t)err;
+}
+
+status_t HWComposer::release() const {
+    int err = mHwc->set(mHwc, NULL, NULL, NULL);
+    return (status_t)err;
+}
+
+size_t HWComposer::getNumLayers() const {
+    return mList ? mList->numHwLayers : 0;
+}
+
+hwc_layer_t* HWComposer::getLayers() const {
+    return mList ? mList->hwLayers : 0;
+}
+
+void HWComposer::dump(String8& result, char* buffer, size_t SIZE) const {
+    if (mHwc && mList) {
+        result.append("Hardware Composer state:\n");
+
+        snprintf(buffer, SIZE, "  numHwLayers=%u, flags=%08x\n",
+                mList->numHwLayers, mList->flags);
+        result.append(buffer);
+
+        for (size_t i=0 ; i<mList->numHwLayers ; i++) {
+            const hwc_layer_t& l(mList->hwLayers[i]);
+            snprintf(buffer, SIZE, "  %8s | %08x | %08x | %02x | %04x | [%5d,%5d,%5d,%5d] |  [%5d,%5d,%5d,%5d]\n",
+                    l.compositionType ? "OVERLAY" : "FB",
+                    l.hints, l.flags, l.transform, l.blending,
+                    l.sourceCrop.left, l.sourceCrop.top, l.sourceCrop.right, l.sourceCrop.bottom,
+                    l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom);
+            result.append(buffer);
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
new file mode 100644
index 0000000..5a9e9eb
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef ANDROID_SF_HWCOMPOSER_H
+#define ANDROID_SF_HWCOMPOSER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+
+#include <hardware/hwcomposer.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class String8;
+
+class HWComposer
+{
+public:
+
+    HWComposer();
+    ~HWComposer();
+
+    status_t initCheck() const;
+
+    // tells the HAL what the framebuffer is
+    void setFrameBuffer(EGLDisplay dpy, EGLSurface sur);
+
+    // create a work list for numLayers layer
+    status_t createWorkList(size_t numLayers);
+
+    // Asks the HAL what it can do
+    status_t prepare() const;
+
+    // commits the list
+    status_t commit() const;
+
+    // release hardware resources
+    status_t release() const;
+
+    size_t getNumLayers() const;
+    hwc_layer_t* getLayers() const;
+
+    // for debugging
+    void dump(String8& out, char* scratch, size_t SIZE) const;
+
+private:
+    hw_module_t const*      mModule;
+    hwc_composer_device_t*  mHwc;
+    hwc_layer_list_t*       mList;
+    size_t                  mCapacity;
+    hwc_display_t           mDpy;
+    hwc_surface_t           mSur;
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a060d31..1b06843 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -35,6 +35,7 @@
 #include "Layer.h"
 #include "SurfaceFlinger.h"
 #include "DisplayHardware/DisplayHardware.h"
+#include "DisplayHardware/HWComposer.h"
 
 
 #define DEBUG_RESIZE    0
@@ -170,7 +171,8 @@
     mReqHeight = h;
 
     mSecure = (flags & ISurfaceComposer::eSecure) ? true : false;
-    mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;
+    mNeedsBlending = (info.h_alpha - info.l_alpha) > 0 &&
+            (flags & ISurfaceComposer::eOpaque) == 0;
 
     // we use the red index
     int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED);
@@ -181,6 +183,62 @@
     return NO_ERROR;
 }
 
+void Layer::setGeometry(hwc_layer_t* hwcl)
+{
+    hwcl->compositionType = HWC_FRAMEBUFFER;
+    hwcl->hints = 0;
+    hwcl->flags = 0;
+    hwcl->transform = 0;
+    hwcl->blending = HWC_BLENDING_NONE;
+
+    // we can't do alpha-fade with the hwc HAL
+    const State& s(drawingState());
+    if (s.alpha < 0xFF) {
+        hwcl->flags = HWC_SKIP_LAYER;
+        return;
+    }
+
+    // we can only handle simple transformation
+    if (mOrientation & Transform::ROT_INVALID) {
+        hwcl->flags = HWC_SKIP_LAYER;
+        return;
+    }
+
+    hwcl->transform = mOrientation;
+
+    if (needsBlending()) {
+        hwcl->blending = mPremultipliedAlpha ?
+                HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE;
+    }
+
+    hwcl->displayFrame.left   = mTransformedBounds.left;
+    hwcl->displayFrame.top    = mTransformedBounds.top;
+    hwcl->displayFrame.right  = mTransformedBounds.right;
+    hwcl->displayFrame.bottom = mTransformedBounds.bottom;
+
+    hwcl->visibleRegionScreen.rects =
+            reinterpret_cast<hwc_rect_t const *>(
+                    visibleRegionScreen.getArray(
+                            &hwcl->visibleRegionScreen.numRects));
+}
+
+void Layer::setPerFrameData(hwc_layer_t* hwcl) {
+    sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
+    if (buffer == NULL) {
+        // this situation can happen if we ran out of memory for instance.
+        // not much we can do. continue to use whatever texture was bound
+        // to this context.
+        hwcl->handle = NULL;
+        return;
+    }
+    hwcl->handle = const_cast<native_handle_t*>(buffer->handle);
+    // TODO: set the crop value properly
+    hwcl->sourceCrop.left   = 0;
+    hwcl->sourceCrop.top    = 0;
+    hwcl->sourceCrop.right  = buffer->width;
+    hwcl->sourceCrop.bottom = buffer->height;
+}
+
 void Layer::reloadTexture(const Region& dirty)
 {
     sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
@@ -321,6 +379,7 @@
         Mutex::Autolock _l(mLock);
 
         // zero means default
+        const bool fixedSize = reqWidth && reqHeight;
         if (!reqFormat) reqFormat = mFormat;
         if (!reqWidth)  reqWidth = mWidth;
         if (!reqHeight) reqHeight = mHeight;
@@ -334,6 +393,7 @@
             mReqWidth  = reqWidth;
             mReqHeight = reqHeight;
             mReqFormat = reqFormat;
+            mFixedSize = fixedSize;
 
             lcblk->reallocateAllExcept(index);
         }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 263c372..caa6d17 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -68,6 +68,8 @@
     bool isFixedSize() const;
 
     // LayerBase interface
+    virtual void setGeometry(hwc_layer_t* hwcl);
+    virtual void setPerFrameData(hwc_layer_t* hwcl);
     virtual void drawForSreenShot() const;
     virtual void onDraw(const Region& clip) const;
     virtual uint32_t doTransaction(uint32_t transactionFlags);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 069b85a..8ab5a10 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -309,6 +309,15 @@
     }
 }
 
+void LayerBase::setGeometry(hwc_layer_t* hwcl) {
+    hwcl->flags |= HWC_SKIP_LAYER;
+}
+
+void LayerBase::setPerFrameData(hwc_layer_t* hwcl) {
+    hwcl->compositionType = HWC_FRAMEBUFFER;
+    hwcl->handle = NULL;
+}
+
 void LayerBase::draw(const Region& clip) const
 {
     // reset GL state
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index d688f65..bdee05b 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -35,6 +35,8 @@
 
 #include <pixelflinger/pixelflinger.h>
 
+#include <hardware/hwcomposer.h>
+
 #include "Transform.h"
 
 namespace android {
@@ -108,6 +110,10 @@
 
     virtual const char* getTypeId() const { return "LayerBase"; }
 
+    virtual void setGeometry(hwc_layer_t* hwcl);
+
+    virtual void setPerFrameData(hwc_layer_t* hwcl);
+
     /**
      * draw - performs some global clipping optimizations
      * and calls onDraw().
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3734969..3df9fe0 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -53,6 +53,7 @@
 #include "SurfaceFlinger.h"
 
 #include "DisplayHardware/DisplayHardware.h"
+#include "DisplayHardware/HWComposer.h"
 
 /* ideally AID_GRAPHICS would be in a semi-public header
  * or there would be a way to map a user/group name to its id
@@ -78,6 +79,7 @@
         mReadFramebuffer("android.permission.READ_FRAME_BUFFER"),
         mDump("android.permission.DUMP"),
         mVisibleRegionsDirty(false),
+        mHwWorkListDirty(false),
         mDeferReleaseConsole(false),
         mFreezeDisplay(false),
         mElectronBeamAnimation(false),
@@ -85,6 +87,7 @@
         mFreezeDisplayTime(0),
         mDebugRegion(0),
         mDebugBackground(0),
+        mDebugDisableHWC(0),
         mDebugInSwapBuffers(0),
         mLastSwapBufferTime(0),
         mDebugInTransaction(0),
@@ -166,7 +169,7 @@
 {
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
-    LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );  
+    LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
     mBootFinished = true;
     property_set("ctl.stop", "bootanim");
 }
@@ -202,10 +205,10 @@
     mServerHeap = new MemoryHeapBase(4096,
             MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap");
     LOGE_IF(mServerHeap==0, "can't create shared memory dealer");
-    
+
     mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase());
     LOGE_IF(mServerCblk==0, "can't get to shared control block's address");
-    
+
     new(mServerCblk) surface_flinger_cblk_t;
 
     // initialize primary screen
@@ -234,7 +237,7 @@
 
     // Initialize OpenGL|ES
     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-    glPixelStorei(GL_PACK_ALIGNMENT, 4); 
+    glPixelStorei(GL_PACK_ALIGNMENT, 4);
     glEnableClientState(GL_VERTEX_ARRAY);
     glEnable(GL_SCISSOR_TEST);
     glShadeModel(GL_FLAT);
@@ -268,7 +271,7 @@
 
     // start boot animation
     property_set("ctl.start", "bootanim");
-    
+
     return NO_ERROR;
 }
 
@@ -371,6 +374,11 @@
     // post surfaces (if needed)
     handlePageFlip();
 
+    if (UNLIKELY(mHwWorkListDirty)) {
+        // build the h/w work list
+        handleWorkList();
+    }
+
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     if (LIKELY(hw.canDraw() && !isFrozen())) {
         // repaint the framebuffer (if needed)
@@ -385,13 +393,12 @@
         logger.log(GraphicLog::SF_COMPOSITION_COMPLETE, index);
         hw.compositionComplete();
 
-        // release the clients before we flip ('cause flip might block)
-        logger.log(GraphicLog::SF_UNLOCK_CLIENTS, index);
-        unlockClients();
-
         logger.log(GraphicLog::SF_SWAP_BUFFERS, index);
         postFramebuffer();
 
+        logger.log(GraphicLog::SF_UNLOCK_CLIENTS, index);
+        unlockClients();
+
         logger.log(GraphicLog::SF_REPAINT_DONE, index);
     } else {
         // pretend we did the post
@@ -460,6 +467,7 @@
         handleTransactionLocked(transactionFlags, ditchedLayers);
         mLastTransactionTime = systemTime() - now;
         mDebugInTransaction = 0;
+        mHwWorkListDirty = true;
         // here the transaction has been committed
     }
 
@@ -467,6 +475,7 @@
      * Clean-up all layers that went away
      * (do this without the lock held)
      */
+
     const size_t count = ditchedLayers.size();
     for (size_t i=0 ; i<count ; i++) {
         if (ditchedLayers[i] != 0) {
@@ -670,7 +679,7 @@
 
         // Update aboveOpaqueLayers for next (lower) layer
         aboveOpaqueLayers.orSelf(opaqueRegion);
-        
+
         // Store the visible region is screen space
         layer->setVisibleRegion(visibleRegion);
         layer->setCoveredRegion(coveredRegion);
@@ -700,8 +709,8 @@
 void SurfaceFlinger::handlePageFlip()
 {
     bool visibleRegions = mVisibleRegionsDirty;
-    LayerVector& currentLayers = const_cast<LayerVector&>(
-            mDrawingState.layersSortedByZ);
+    LayerVector& currentLayers(
+            const_cast<LayerVector&>(mDrawingState.layersSortedByZ));
     visibleRegions |= lockPageFlip(currentLayers);
 
         const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -724,6 +733,7 @@
 
             mWormholeRegion = screenRegion.subtract(opaqueRegion);
             mVisibleRegionsDirty = false;
+            mHwWorkListDirty = true;
         }
 
     unlockPageFlip(currentLayers);
@@ -754,6 +764,24 @@
     }
 }
 
+void SurfaceFlinger::handleWorkList()
+{
+    mHwWorkListDirty = false;
+    HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
+    if (hwc.initCheck() == NO_ERROR) {
+        const Vector< sp<LayerBase> >& currentLayers(mVisibleLayersSortedByZ);
+        const size_t count = currentLayers.size();
+        hwc.createWorkList(count);
+        hwc_layer_t* const cur(hwc.getLayers());
+        for (size_t i=0 ; cur && i<count ; i++) {
+            currentLayers[i]->setGeometry(&cur[i]);
+            if (mDebugDisableHWC) {
+                cur[i].compositionType = HWC_FRAMEBUFFER;
+                cur[i].flags |= HWC_SKIP_LAYER;
+            }
+        }
+    }
+}
 
 void SurfaceFlinger::handleRepaint()
 {
@@ -774,8 +802,8 @@
     glLoadIdentity();
 
     uint32_t flags = hw.getFlags();
-    if ((flags & DisplayHardware::SWAP_RECTANGLE) || 
-        (flags & DisplayHardware::BUFFER_PRESERVED)) 
+    if ((flags & DisplayHardware::SWAP_RECTANGLE) ||
+        (flags & DisplayHardware::BUFFER_PRESERVED))
     {
         // we can redraw only what's dirty, but since SWAP_RECTANGLE only
         // takes a rectangle, we must make sure to update that whole
@@ -818,9 +846,73 @@
         // draw something...
         drawWormhole();
     }
+
+    status_t err = NO_ERROR;
     const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; ++i) {
+    size_t count = layers.size();
+
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    HWComposer& hwc(hw.getHwComposer());
+    hwc_layer_t* const cur(hwc.getLayers());
+
+    LOGE_IF(cur && hwc.getNumLayers() != count,
+            "HAL number of layers (%d) doesn't match surfaceflinger (%d)",
+            hwc.getNumLayers(), count);
+
+    // just to be extra-safe, use the smallest count
+    if (hwc.initCheck() == NO_ERROR) {
+        count = count < hwc.getNumLayers() ? count : hwc.getNumLayers();
+    }
+
+    /*
+     *  update the per-frame h/w composer data for each layer
+     *  and build the transparent region of the FB
+     */
+    Region transparent;
+    if (cur) {
+        for (size_t i=0 ; i<count ; i++) {
+            const sp<LayerBase>& layer(layers[i]);
+            layer->setPerFrameData(&cur[i]);
+            if (cur[i].hints & HWC_HINT_CLEAR_FB) {
+                if (!(layer->needsBlending())) {
+                    transparent.orSelf(layer->visibleRegionScreen);
+                }
+            }
+        }
+        err = hwc.prepare();
+        LOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
+    }
+
+    /*
+     *  clear the area of the FB that need to be transparent
+     */
+    transparent.andSelf(dirty);
+    if (!transparent.isEmpty()) {
+        glClearColor(0,0,0,0);
+        Region::const_iterator it = transparent.begin();
+        Region::const_iterator const end = transparent.end();
+        const int32_t height = hw.getHeight();
+        while (it != end) {
+            const Rect& r(*it++);
+            const GLint sy = height - (r.top + r.height());
+            glScissor(r.left, sy, r.width(), r.height());
+            glClear(GL_COLOR_BUFFER_BIT);
+        }
+    }
+
+
+    /*
+     * and then, render the layers targeted at the framebuffer
+     */
+    for (size_t i=0 ; i<count ; i++) {
+        if (cur) {
+            if ((cur[i].compositionType != HWC_FRAMEBUFFER) &&
+                !(cur[i].flags & HWC_SKIP_LAYER)) {
+                // skip layers handled by the HAL
+                continue;
+            }
+        }
+
         const sp<LayerBase>& layer(layers[i]);
         const Region clip(dirty.intersect(layer->visibleRegionScreen));
         if (!clip.isEmpty()) {
@@ -1059,7 +1151,7 @@
     if (android_atomic_dec(&mTransactionCount) == 1) {
         signalEvent();
 
-        // if there is a transaction with a resize, wait for it to 
+        // if there is a transaction with a resize, wait for it to
         // take effect before returning.
         Mutex::Autolock _l(mStateLock);
         while (mResizeTransationPending) {
@@ -1103,7 +1195,7 @@
     return NO_ERROR;
 }
 
-int SurfaceFlinger::setOrientation(DisplayID dpy, 
+int SurfaceFlinger::setOrientation(DisplayID dpy,
         int orientation, uint32_t flags)
 {
     if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
@@ -1136,14 +1228,17 @@
                 int(w), int(h));
         return surfaceHandle;
     }
-    
+
     //LOGD("createSurface for pid %d (%d x %d)", pid, w, h);
     sp<Layer> normalLayer;
     switch (flags & eFXSurfaceMask) {
         case eFXSurfaceNormal:
+#if HAS_PUSH_BUFFERS
             if (UNLIKELY(flags & ePushBuffers)) {
                 layer = createPushBuffersSurface(client, d, w, h, flags);
-            } else {
+            } else
+#endif
+            {
                 normalLayer = createNormalSurface(client, d, w, h, flags, format);
                 layer = normalLayer;
             }
@@ -1162,7 +1257,7 @@
         ssize_t token = addClientLayer(client, layer);
 
         surfaceHandle = layer->getSurface();
-        if (surfaceHandle != 0) { 
+        if (surfaceHandle != 0) {
             params->token = token;
             params->identity = surfaceHandle->getIdentity();
             params->width = w;
@@ -1246,7 +1341,7 @@
     /*
      * called by the window manager, when a surface should be marked for
      * destruction.
-     * 
+     *
      * The surface is removed from the current and drawing lists, but placed
      * in the purgatory queue, so it's not destroyed right-away (we need
      * to wait for all client's references to go away first).
@@ -1267,7 +1362,7 @@
 status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer)
 {
     // called by ~ISurface() when all references are gone
-    
+
     class MessageDestroySurface : public MessageBase {
         SurfaceFlinger* flinger;
         sp<LayerBaseClient> layer;
@@ -1280,9 +1375,9 @@
             layer.clear(); // clear it outside of the lock;
             Mutex::Autolock _l(flinger->mStateLock);
             /*
-             * remove the layer from the current list -- chances are that it's 
-             * not in the list anyway, because it should have been removed 
-             * already upon request of the client (eg: window manager). 
+             * remove the layer from the current list -- chances are that it's
+             * not in the list anyway, because it should have been removed
+             * already upon request of the client (eg: window manager).
              * However, a buggy client could have not done that.
              * Since we know we don't have any more clients, we don't need
              * to use the purgatory.
@@ -1397,7 +1492,7 @@
         }
         const bool locked(retry >= 0);
         if (!locked) {
-            snprintf(buffer, SIZE, 
+            snprintf(buffer, SIZE,
                     "SurfaceFlinger appears to be unresponsive, "
                     "dumping anyways (no locks held)\n");
             result.append(buffer);
@@ -1439,6 +1534,13 @@
             result.append(buffer);
         }
 
+        HWComposer& hwc(hw.getHwComposer());
+        snprintf(buffer, SIZE, "  h/w composer %s and %s\n",
+                hwc.initCheck()==NO_ERROR ? "present" : "not present",
+                mDebugDisableHWC ? "disabled" : "enabled");
+        result.append(buffer);
+        hwc.dump(result, buffer, SIZE);
+
         const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
         alloc.dump(result);
 
@@ -1514,6 +1616,11 @@
                 n = data.readInt32();
                 mDebugBackground = n ? 1 : 0;
                 return NO_ERROR;
+            case 1008:  // toggle use of hw composer
+                n = data.readInt32();
+                mDebugDisableHWC = n ? 1 : 0;
+                mHwWorkListDirty = true;
+                // fall-through...
             case 1004:{ // repaint everything
                 Mutex::Autolock _l(mStateLock);
                 const DisplayHardware& hw(graphicPlane(0).displayHardware());
@@ -2298,12 +2405,15 @@
 {
     int32_t name = NAME_NOT_FOUND;
     sp<Layer> layer(mFlinger->getLayer(sur));
-    if (layer == 0) return name;
+    if (layer == 0) {
+        return name;
+    }
 
     // if this layer already has a token, just return it
     name = layer->getToken();
-    if ((name >= 0) && (layer->getClient() == this))
+    if ((name >= 0) && (layer->getClient() == this)) {
         return name;
+    }
 
     name = 0;
     do {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d07a3ad..2ebcc36 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -306,6 +306,7 @@
             void        handlePageFlip();
             bool        lockPageFlip(const LayerVector& currentLayers);
             void        unlockPageFlip(const LayerVector& currentLayers);
+            void        handleWorkList();
             void        handleRepaint();
             void        postFramebuffer();
             void        composeSurfaces(const Region& dirty);
@@ -393,6 +394,7 @@
                 Region                      mInvalidRegion;
                 Region                      mWormholeRegion;
                 bool                        mVisibleRegionsDirty;
+                bool                        mHwWorkListDirty;
                 bool                        mDeferReleaseConsole;
                 bool                        mFreezeDisplay;
                 bool                        mElectronBeamAnimation;
@@ -404,6 +406,7 @@
                 // don't use a lock for these, we don't care
                 int                         mDebugRegion;
                 int                         mDebugBackground;
+                int                         mDebugDisableHWC;
                 volatile nsecs_t            mDebugInSwapBuffers;
                 nsecs_t                     mLastSwapBufferTime;
                 volatile nsecs_t            mDebugInTransaction;