Merge "fix broken logTimeStat stuff"
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index 168310c..6ed3c6f 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -62,8 +62,11 @@
     // contents of the buffer associated with slot and transfers ownership of
     // that slot back to the server. It is not valid to call queueBuffer on a
     // slot that is not owned by the client or one for which a buffer associated
-    // via requestBuffer.
-    virtual status_t queueBuffer(int slot) = 0;
+    // via requestBuffer. In addition, a timestamp must be provided by the
+    // client for this buffer. The timestamp is measured in nanoseconds, and
+    // must be monotonically increasing. Its other properties (zero point, etc)
+    // are client-dependent, and should be documented by the client.
+    virtual status_t queueBuffer(int slot, int64_t timestamp) = 0;
 
     // cancelBuffer indicates that the client does not wish to fill in the
     // buffer associated with slot and transfers ownership of the slot back to
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 9bf38f7..afa64d3 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -66,7 +66,12 @@
     // unmodified.
     virtual status_t dequeueBuffer(int *buf);
 
-    virtual status_t queueBuffer(int buf);
+    // queueBuffer returns a filled buffer to the SurfaceTexture. In addition, a
+    // timestamp must be provided for the buffer. The timestamp is in
+    // nanoseconds, and must be monotonically increasing. Its other semantics
+    // (zero point, etc) are client-dependent and should be documented by the
+    // client.
+    virtual status_t queueBuffer(int buf, int64_t timestamp);
     virtual void cancelBuffer(int buf);
     virtual status_t setCrop(const Rect& reg);
     virtual status_t setTransform(uint32_t transform);
@@ -98,6 +103,14 @@
     // functions.
     void getTransformMatrix(float mtx[16]);
 
+    // getTimestamp retrieves the timestamp associated with the texture image
+    // set by the most recent call to updateTexImage.
+    //
+    // The timestamp is in nanoseconds, and is monotonically increasing. Its
+    // other semantics (zero point, etc) are source-dependent and should be
+    // documented by the source.
+    int64_t getTimestamp();
+
     // setFrameAvailableListener sets the listener object that will be notified
     // when a new frame becomes available.
     void setFrameAvailableListener(const sp<FrameAvailableListener>& l);
@@ -172,6 +185,10 @@
     // gets set to mLastQueuedTransform each time updateTexImage is called.
     uint32_t mCurrentTransform;
 
+    // mCurrentTimestamp is the timestamp for the current texture. It
+    // gets set to mLastQueuedTimestamp each time updateTexImage is called.
+    int64_t mCurrentTimestamp;
+
     // mLastQueued is the buffer slot index of the most recently enqueued buffer.
     // At construction time it is initialized to INVALID_BUFFER_SLOT, and is
     // updated each time queueBuffer is called.
@@ -187,6 +204,10 @@
     // queueBuffer gets called.
     uint32_t mLastQueuedTransform;
 
+    // mLastQueuedTimestamp is the timestamp for the buffer that was most
+    // recently queued. This gets set by queueBuffer.
+    int64_t mLastQueuedTimestamp;
+
     // mNextCrop is the crop rectangle that will be used for the next buffer
     // that gets queued. It is set by calling setCrop.
     Rect mNextCrop;
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index 4cdece9..df82bf2 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -33,6 +33,8 @@
 public:
     SurfaceTextureClient(const sp<ISurfaceTexture>& surfaceTexture);
 
+    sp<ISurfaceTexture> getISurfaceTexture() const;
+
 private:
 
     // can't be copied
@@ -61,6 +63,7 @@
     int dispatchSetBufferCount(va_list args);
     int dispatchSetBuffersGeometry(va_list args);
     int dispatchSetBuffersTransform(va_list args);
+    int dispatchSetBuffersTimestamp(va_list args);
     int dispatchSetCrop(va_list args);
     int dispatchSetUsage(va_list args);
 
@@ -69,6 +72,7 @@
     int setBufferCount(int bufferCount);
     int setBuffersGeometry(int w, int h, int format);
     int setBuffersTransform(int transform);
+    int setBuffersTimestamp(int64_t timestamp);
     int setCrop(Rect const* rect);
     int setUsage(uint32_t reqUsage);
 
@@ -112,6 +116,11 @@
     // at the next deuque operation. It is initialized to 0.
     uint32_t mReqUsage;
 
+    // mTimestamp is the timestamp that will be used for the next buffer queue
+    // operation. It defaults to NATIVE_WINDOW_TIMESTAMP_AUTO, which means that
+    // a timestamp is auto-generated when queueBuffer is called.
+    int64_t mTimestamp;
+
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of SurfaceTexture objects. It must be locked whenever the
     // member variables are accessed.
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 9e0b5bb..a59d9e5 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -226,7 +226,8 @@
     int  dispatch_set_buffer_count(va_list args);
     int  dispatch_set_buffers_geometry(va_list args);
     int  dispatch_set_buffers_transform(va_list args);
-    
+    int  dispatch_set_buffers_timestamp(va_list args);
+
     void setUsage(uint32_t reqUsage);
     int  connect(int api);
     int  disconnect(int api);
@@ -234,6 +235,7 @@
     int  setBufferCount(int bufferCount);
     int  setBuffersGeometry(int w, int h, int format);
     int  setBuffersTransform(int transform);
+    int  setBuffersTimestamp(int64_t timestamp);
 
     /*
      *  private stuff...
diff --git a/include/ui/Input.h b/include/ui/Input.h
index e9cacf4..8e8b61b 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -311,6 +311,13 @@
 
     inline int32_t getAction() const { return mAction; }
 
+    inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; }
+
+    inline int32_t getActionIndex() const {
+        return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+                >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+    }
+
     inline void setAction(int32_t action) { mAction = action; }
 
     inline int32_t getFlags() const { return mFlags; }
@@ -458,6 +465,8 @@
                 AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex);
     }
 
+    ssize_t findPointerIndex(int32_t pointerId) const;
+
     void initialize(
             int32_t deviceId,
             int32_t source,
@@ -551,8 +560,7 @@
 };
 
 /*
- * Calculates the velocity of pointer motions over time.
- * Uses essentially the same algorithm as android.view.VelocityTracker.
+ * Calculates the velocity of pointer movements over time.
  */
 class VelocityTracker {
 public:
@@ -565,6 +573,11 @@
     // Resets the velocity tracker state.
     void clear();
 
+    // Resets the velocity tracker state for specific pointers.
+    // Call this method when some pointers have changed and may be reusing
+    // an id that was assigned to a different pointer earlier.
+    void clearPointers(BitSet32 idBits);
+
     // Adds movement information for a set of pointers.
     // The idBits bitfield specifies the pointer ids of the pointers whose positions
     // are included in the movement.
@@ -572,11 +585,20 @@
     // increasing id.  Its size should be equal to the number of one bits in idBits.
     void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions);
 
+    // Adds movement information for all pointers in a MotionEvent, including historical samples.
+    void addMovement(const MotionEvent* event);
+
     // Gets the velocity of the specified pointer id in position units per second.
     // Returns false and sets the velocity components to zero if there is no movement
     // information for the pointer.
     bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
 
+    // Gets the active pointer id, or -1 if none.
+    inline int32_t getActivePointerId() const { return mActivePointerId; }
+
+    // Gets a bitset containing all pointer ids from the most recent movement.
+    inline BitSet32 getCurrentPointerIdBits() const { return mMovements[mIndex].idBits; }
+
 private:
     // Number of samples to keep.
     static const uint32_t HISTORY_SIZE = 10;
@@ -585,7 +607,7 @@
     static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms
 
     // The minimum duration between samples when estimating velocity.
-    static const nsecs_t MIN_DURATION = 5 * 1000000; // 5 ms
+    static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
 
     struct Movement {
         nsecs_t eventTime;
@@ -595,6 +617,7 @@
 
     uint32_t mIndex;
     Movement mMovements[HISTORY_SIZE];
+    int32_t mActivePointerId;
 };
 
 /*
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index b912e9b..8383957 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -228,6 +228,9 @@
     { "BUTTON_14", 201 },
     { "BUTTON_15", 202 },
     { "BUTTON_16", 203 },
+    { "LANGUAGE_SWITCH", 204 },
+    { "MANNER_MODE", 205 },
+    { "3D_MODE", 206 },
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index 0ac34d0..0a6e4fb 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -57,7 +57,7 @@
 {
     /* a magic value defined by the actual EGL native type */
     int magic;
-    
+
     /* the sizeof() of the actual EGL native type */
     int version;
 
@@ -110,6 +110,14 @@
      * conjunction with this query.
      */
     NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+
+    /* Get the concrete type of a ANativeWindow.  See below for the list of
+     * possible return values.
+     *
+     * This query should not be used outside the Android framework and will
+     * likely be removed in the near future.
+     */
+    NATIVE_WINDOW_CONCRETE_TYPE,
 };
 
 /* valid operations for the (*perform)() hook */
@@ -121,6 +129,7 @@
     NATIVE_WINDOW_SET_BUFFER_COUNT,
     NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
     NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
+    NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
 };
 
 /* parameter for NATIVE_WINDOW_[DIS]CONNECT */
@@ -142,7 +151,22 @@
     NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
 };
 
-struct ANativeWindow 
+/* values returned by the NATIVE_WINDOW_CONCRETE_TYPE query */
+enum {
+    NATIVE_WINDOW_FRAMEBUFFER,                  // FramebufferNativeWindow
+    NATIVE_WINDOW_SURFACE,                      // Surface
+    NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT,       // SurfaceTextureClient
+};
+
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
+ *
+ * Special timestamp value to indicate that timestamps should be auto-generated
+ * by the native window when queueBuffer is called.  This is equal to INT64_MIN,
+ * defined directly to avoid problems with C99/C++ inclusion of stdint.h.
+ */
+const int64_t NATIVE_WINDOW_TIMESTAMP_AUTO = (-9223372036854775807LL-1);
+
+struct ANativeWindow
 {
 #ifdef __cplusplus
     ANativeWindow()
@@ -247,7 +271,8 @@
      *     NATIVE_WINDOW_SET_BUFFER_COUNT
      *     NATIVE_WINDOW_SET_BUFFERS_GEOMETRY
      *     NATIVE_WINDOW_SET_BUFFERS_TRANSFORM
-     *  
+     *     NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP
+     *
      */
     
     int     (*perform)(struct ANativeWindow* window,
@@ -374,6 +399,22 @@
             transform);
 }
 
+/*
+ * native_window_set_buffers_timestamp(..., int64_t timestamp)
+ * All buffers queued after this call will be associated with the timestamp
+ * parameter specified. If the timestamp is set to NATIVE_WINDOW_TIMESTAMP_AUTO
+ * (the default), timestamps will be generated automatically when queueBuffer is
+ * called. The timestamp is measured in nanoseconds, and must be monotonically
+ * increasing.
+ */
+static inline int native_window_set_buffers_timestamp(
+        ANativeWindow* window,
+        int64_t timestamp)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
+            timestamp);
+}
+
 // ---------------------------------------------------------------------------
 
 /* FIXME: this is legacy for pixmaps */
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
index f03825a..de748b5 100644
--- a/include/utils/BitSet.h
+++ b/include/utils/BitSet.h
@@ -61,6 +61,10 @@
     // Result is undefined if all bits are marked.
     inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }
 
+    // Finds the last marked bit in the set.
+    // Result is undefined if all bits are unmarked.
+    inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); }
+
     // Gets the index of the specified bit in the set, which is the number of
     // marked bits that appear before the specified bit.
     inline uint32_t getIndexOfBit(uint32_t n) const {
diff --git a/include/utils/Functor.h b/include/utils/Functor.h
index 565f4a3..e24ded4 100644
--- a/include/utils/Functor.h
+++ b/include/utils/Functor.h
@@ -25,8 +25,7 @@
 public:
     Functor() {}
     virtual ~Functor() {}
-    virtual status_t operator ()() { return true; }
-    virtual status_t operator ()(float* data, uint32_t len) { return true; }
+    virtual status_t operator ()(int what, void* data) { return NO_ERROR; }
 };
 
 }; // namespace android
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 6b49ff5..4163697 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -165,8 +165,8 @@
     String8 walkPath(String8* outRemains = NULL) const;
 
     /*
-     * Return the filename extension.  This is the last '.' and up to
-     * four characters that follow it.  The '.' is included in case we
+     * Return the filename extension.  This is the last '.' and any number
+     * of characters that follow it.  The '.' is included in case we
      * decide to expand our definition of what constitutes an extension.
      *
      * "/tmp/foo/bar.c" --> ".c"
diff --git a/include/utils/Timers.h b/include/utils/Timers.h
index 9a9e07c..8b4d322 100644
--- a/include/utils/Timers.h
+++ b/include/utils/Timers.h
@@ -88,6 +88,16 @@
 nsecs_t systemTime(int clock);
 #endif // def __cplusplus
 
+/**
+ * Returns the number of milliseconds to wait between the reference time and the timeout time.
+ * If the timeout is in the past relative to the reference time, returns 0.
+ * If the timeout is more than INT_MAX milliseconds in the future relative to the reference time,
+ * such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay.
+ * Otherwise, returns the difference between the reference time and timeout time
+ * rounded up to the next millisecond.
+ */
+int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/include/utils/Vector.h b/include/utils/Vector.h
index ec851bd..6fd307f 100644
--- a/include/utils/Vector.h
+++ b/include/utils/Vector.h
@@ -162,6 +162,9 @@
      inline status_t        sort(compar_t cmp);
      inline status_t        sort(compar_r_t cmp, void* state);
 
+     // for debugging only
+     inline size_t getItemSize() const { return itemSize(); }
+
 protected:
     virtual void    do_construct(void* storage, size_t num) const;
     virtual void    do_destroy(void* storage, size_t num) const;
diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp
index d661fd5..bc14ad5 100644
--- a/libs/gui/ISurfaceTexture.cpp
+++ b/libs/gui/ISurfaceTexture.cpp
@@ -88,10 +88,11 @@
         return result;
     }
 
-    virtual status_t queueBuffer(int buf) {
+    virtual status_t queueBuffer(int buf, int64_t timestamp) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
         data.writeInt32(buf);
+        data.writeInt64(timestamp);
         remote()->transact(QUEUE_BUFFER, data, &reply);
         status_t result = reply.readInt32();
         return result;
@@ -174,7 +175,8 @@
         case QUEUE_BUFFER: {
             CHECK_INTERFACE(ISurfaceTexture, data, reply);
             int buf = data.readInt32();
-            status_t result = queueBuffer(buf);
+            int64_t timestamp = data.readInt64();
+            status_t result = queueBuffer(buf, timestamp);
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
@@ -196,7 +198,6 @@
             return NO_ERROR;
         } break;
         case SET_TRANSFORM: {
-            Rect reg;
             CHECK_INTERFACE(ISurfaceTexture, data, reply);
             uint32_t transform = data.readInt32();
             status_t result = setTransform(transform);
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 3bed959..cdaca47 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -76,9 +76,15 @@
 static void mtxMul(float out[16], const float a[16], const float b[16]);
 
 SurfaceTexture::SurfaceTexture(GLuint tex) :
-    mBufferCount(MIN_BUFFER_SLOTS), mCurrentTexture(INVALID_BUFFER_SLOT),
-    mCurrentTransform(0), mLastQueued(INVALID_BUFFER_SLOT),
-    mLastQueuedTransform(0), mNextTransform(0), mTexName(tex) {
+    mBufferCount(MIN_BUFFER_SLOTS),
+    mCurrentTexture(INVALID_BUFFER_SLOT),
+    mCurrentTransform(0),
+    mCurrentTimestamp(0),
+    mLastQueued(INVALID_BUFFER_SLOT),
+    mLastQueuedTransform(0),
+    mLastQueuedTimestamp(0),
+    mNextTransform(0),
+    mTexName(tex) {
     LOGV("SurfaceTexture::SurfaceTexture");
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
         mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
@@ -153,7 +159,7 @@
     return OK;
 }
 
-status_t SurfaceTexture::queueBuffer(int buf) {
+status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp) {
     LOGV("SurfaceTexture::queueBuffer");
     Mutex::Autolock lock(mMutex);
     if (buf < 0 || mBufferCount <= buf) {
@@ -172,6 +178,7 @@
     mLastQueued = buf;
     mLastQueuedCrop = mNextCrop;
     mLastQueuedTransform = mNextTransform;
+    mLastQueuedTimestamp = timestamp;
     if (mFrameAvailableListener != 0) {
         mFrameAvailableListener->onFrameAvailable();
     }
@@ -246,12 +253,13 @@
         mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
         mCurrentCrop = mLastQueuedCrop;
         mCurrentTransform = mLastQueuedTransform;
+        mCurrentTimestamp = mLastQueuedTimestamp;
     }
     return OK;
 }
 
 void SurfaceTexture::getTransformMatrix(float mtx[16]) {
-    LOGV("SurfaceTexture::updateTexImage");
+    LOGV("SurfaceTexture::getTransformMatrix");
     Mutex::Autolock lock(mMutex);
 
     float xform[16];
@@ -283,11 +291,42 @@
     sp<GraphicBuffer>& buf(mSlots[mCurrentTexture].mGraphicBuffer);
     float tx, ty, sx, sy;
     if (!mCurrentCrop.isEmpty()) {
-        tx = float(mCurrentCrop.left) / float(buf->getWidth());
-        ty = float(buf->getHeight() - mCurrentCrop.bottom) /
-                float(buf->getHeight());
-        sx = float(mCurrentCrop.width()) / float(buf->getWidth());
-        sy = float(mCurrentCrop.height()) / float(buf->getHeight());
+        // In order to prevent bilinear sampling at the of the crop rectangle we
+        // may need to shrink it by 2 texels in each direction.  Normally this
+        // would just need to take 1/2 a texel off each end, but because the
+        // chroma channels will likely be subsampled we need to chop off a whole
+        // texel.  This will cause artifacts if someone does nearest sampling
+        // with 1:1 pixel:texel ratio, but it's impossible to simultaneously
+        // accomodate the bilinear and nearest sampling uses.
+        //
+        // If nearest sampling turns out to be a desirable usage of these
+        // textures then we could add the ability to switch a SurfaceTexture to
+        // nearest-mode.  Preferably, however, the image producers (video
+        // decoder, camera, etc.) would simply not use a crop rectangle (or at
+        // least not tell the framework about it) so that the GPU can do the
+        // correct edge behavior.
+        int xshrink = 0, yshrink = 0;
+        if (mCurrentCrop.left > 0) {
+            tx = float(mCurrentCrop.left + 1) / float(buf->getWidth());
+            xshrink++;
+        } else {
+            tx = 0.0f;
+        }
+        if (mCurrentCrop.right < buf->getWidth()) {
+            xshrink++;
+        }
+        if (mCurrentCrop.bottom < buf->getHeight()) {
+            ty = (float(buf->getHeight() - mCurrentCrop.bottom) + 1.0f) /
+                    float(buf->getHeight());
+            yshrink++;
+        } else {
+            ty = 0.0f;
+        }
+        if (mCurrentCrop.top > 0) {
+            yshrink++;
+        }
+        sx = float(mCurrentCrop.width() - xshrink) / float(buf->getWidth());
+        sy = float(mCurrentCrop.height() - yshrink) / float(buf->getHeight());
     } else {
         tx = 0.0f;
         ty = 0.0f;
@@ -298,7 +337,7 @@
         sx, 0, 0, 0,
         0, sy, 0, 0,
         0, 0, 1, 0,
-        sx*tx, sy*ty, 0, 1,
+        tx, ty, 0, 1,
     };
 
     float mtxBeforeFlipV[16];
@@ -311,6 +350,12 @@
     mtxMul(mtx, mtxFlipV, mtxBeforeFlipV);
 }
 
+nsecs_t SurfaceTexture::getTimestamp() {
+    LOGV("SurfaceTexture::getTimestamp");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTimestamp;
+}
+
 void SurfaceTexture::setFrameAvailableListener(
         const sp<FrameAvailableListener>& l) {
     LOGV("SurfaceTexture::setFrameAvailableListener");
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index a40fac9..a4812d0 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -26,7 +26,8 @@
 SurfaceTextureClient::SurfaceTextureClient(
         const sp<ISurfaceTexture>& surfaceTexture):
         mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(1),
-        mReqHeight(1), mReqFormat(DEFAULT_FORMAT), mReqUsage(0), mMutex() {
+        mReqHeight(1), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
+        mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mMutex() {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = setSwapInterval;
     ANativeWindow::dequeueBuffer    = dequeueBuffer;
@@ -40,6 +41,10 @@
     mAllocator = mSurfaceTexture->getAllocator();
 }
 
+sp<ISurfaceTexture> SurfaceTextureClient::getISurfaceTexture() const {
+    return mSurfaceTexture;
+}
+
 int SurfaceTextureClient::setSwapInterval(ANativeWindow* window, int interval) {
     SurfaceTextureClient* c = getSelf(window);
     return c->setSwapInterval(interval);
@@ -131,9 +136,17 @@
 int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) {
     LOGV("SurfaceTextureClient::queueBuffer");
     Mutex::Autolock lock(mMutex);
+    int64_t timestamp;
+    if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
+        timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+        LOGV("SurfaceTextureClient::queueBuffer making up timestamp: %.2f ms",
+             timestamp / 1000000.f);
+    } else {
+        timestamp = mTimestamp;
+    }
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
         if (mSlots[i]->handle == buffer->handle) {
-            return mSurfaceTexture->queueBuffer(i);
+            return mSurfaceTexture->queueBuffer(i, timestamp);
         }
     }
     LOGE("queueBuffer: unknown buffer queued");
@@ -160,6 +173,9 @@
         // SurfaceTextureClient currently never queues frames to SurfaceFlinger.
         *value = 0;
         return NO_ERROR;
+    case NATIVE_WINDOW_CONCRETE_TYPE:
+        *value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT;
+        return NO_ERROR;
     }
     return BAD_VALUE;
 }
@@ -189,6 +205,9 @@
     case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
         res = dispatchSetBuffersTransform(args);
         break;
+    case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+        res = dispatchSetBuffersTimestamp(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -233,6 +252,11 @@
     return setBuffersTransform(transform);
 }
 
+int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) {
+    int64_t timestamp = va_arg(args, int64_t);
+    return setBuffersTimestamp(timestamp);
+}
+
 int SurfaceTextureClient::connect(int api) {
     LOGV("SurfaceTextureClient::connect");
     // XXX: Implement this!
@@ -316,6 +340,14 @@
     return err;
 }
 
+int SurfaceTextureClient::setBuffersTimestamp(int64_t timestamp)
+{
+    LOGV("SurfaceTextureClient::setBuffersTimestamp");
+    Mutex::Autolock lock(mMutex);
+    mTimestamp = timestamp;
+    return NO_ERROR;
+}
+
 void SurfaceTextureClient::freeAllBuffers() {
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
         mSlots[i] = 0;
diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk
index 1dd8885..7516299 100644
--- a/libs/gui/tests/Android.mk
+++ b/libs/gui/tests/Android.mk
@@ -4,39 +4,37 @@
 
 ifneq ($(TARGET_SIMULATOR),true)
 
-# Build the unit tests.
-test_src_files := \
-    SurfaceTextureClient_test.cpp \
+LOCAL_MODULE := SurfaceTexture_test
 
-shared_libraries := \
-	libcutils \
-	libutils \
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+    SurfaceTextureClient_test.cpp \
+    SurfaceTexture_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+	libEGL \
+	libGLESv2 \
+	libandroid \
 	libbinder \
+	libcutils \
 	libgui \
 	libstlport \
+	libsurfaceflinger_client \
+	libui \
+	libutils \
 
-static_libraries := \
+LOCAL_STATIC_LIBRARIES := \
 	libgtest \
 	libgtest_main \
 
-c_includes := \
+LOCAL_C_INCLUDES := \
     bionic \
     bionic/libstdc++/include \
     external/gtest/include \
     external/stlport/stlport \
 
-module_tags := tests
-
-$(foreach file,$(test_src_files), \
-    $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
-    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
-    $(eval LOCAL_C_INCLUDES := $(c_includes)) \
-    $(eval LOCAL_SRC_FILES := $(file)) \
-    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
-    $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
-    $(eval include $(BUILD_EXECUTABLE)) \
-)
+include $(BUILD_EXECUTABLE)
 
 # Build the manual test programs.
 include $(call all-subdir-makefiles)
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 0f140ff..94b05bc 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-#include <gui/SurfaceTextureClient.h>
+#include <EGL/egl.h>
 #include <gtest/gtest.h>
+#include <gui/SurfaceTextureClient.h>
 
 namespace android {
 
@@ -35,6 +36,11 @@
     sp<SurfaceTextureClient> mSTC;
 };
 
+TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
+    sp<ISurfaceTexture> ist(mSTC->getISurfaceTexture());
+    ASSERT_TRUE(ist != NULL);
+}
+
 TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) {
     sp<ANativeWindow> anw(mSTC);
     int result = -123;
@@ -44,4 +50,54 @@
     EXPECT_EQ(0, result);
 }
 
+TEST_F(SurfaceTextureClientTest, ConcreteTypeIsSurfaceTextureClient) {
+    sp<ANativeWindow> anw(mSTC);
+    int result = -123;
+    int err = anw->query(anw.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result);
+    EXPECT_EQ(NO_ERROR, err);
+    EXPECT_EQ(NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT, result);
+}
+
+TEST_F(SurfaceTextureClientTest, ANativeWindowLockFails) {
+    sp<ANativeWindow> anw(mSTC);
+    ANativeWindow_Buffer buf;
+    ASSERT_EQ(BAD_VALUE, ANativeWindow_lock(anw.get(), &buf, NULL));
+}
+
+TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceFails) {
+    sp<ANativeWindow> anw(mSTC);
+
+    EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_DISPLAY, dpy);
+
+    EGLint majorVersion;
+    EGLint minorVersion;
+    EXPECT_TRUE(eglInitialize(dpy, &majorVersion, &minorVersion));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    EGLConfig myConfig = {0};
+    EGLint numConfigs = 0;
+    EGLint configAttribs[] = {
+        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+        EGL_RED_SIZE, 8,
+        EGL_GREEN_SIZE, 8,
+        EGL_BLUE_SIZE, 8,
+        EGL_ALPHA_SIZE, 8,
+        EGL_DEPTH_SIZE, 16,
+        EGL_STENCIL_SIZE, 8,
+        EGL_NONE };
+    EXPECT_TRUE(eglChooseConfig(dpy, configAttribs, &myConfig, 1,
+            &numConfigs));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, anw.get(),
+            NULL);
+    ASSERT_EQ(EGL_NO_SURFACE, eglSurface);
+    ASSERT_EQ(EGL_BAD_NATIVE_WINDOW, eglGetError());
+
+    eglTerminate(dpy);
+}
+
 }
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
new file mode 100644
index 0000000..4184463
--- /dev/null
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <gui/SurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/String8.h>
+
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <ui/FramebufferNativeWindow.h>
+
+namespace android {
+
+class GLTest : public ::testing::Test {
+protected:
+
+    GLTest():
+            mEglDisplay(EGL_NO_DISPLAY),
+            mEglSurface(EGL_NO_SURFACE),
+            mEglContext(EGL_NO_CONTEXT) {
+    }
+
+    virtual void SetUp() {
+        EGLBoolean returnValue;
+
+        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+        EGLint majorVersion;
+        EGLint minorVersion;
+        EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        RecordProperty("EglVersionMajor", majorVersion);
+        RecordProperty("EglVersionMajor", minorVersion);
+
+        EGLConfig myConfig = {0};
+        EGLint numConfigs = 0;
+        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
+                1, &numConfigs));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
+        if (displaySecsEnv != NULL) {
+            mDisplaySecs = atoi(displaySecsEnv);
+            if (mDisplaySecs < 0) {
+                mDisplaySecs = 0;
+            }
+        } else {
+            mDisplaySecs = 0;
+        }
+
+        if (mDisplaySecs > 0) {
+            mComposerClient = new SurfaceComposerClient;
+            ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+            mSurfaceControl = mComposerClient->createSurface(getpid(),
+                    String8("Test Surface"), 0,
+                    getSurfaceWidth(), getSurfaceHeight(),
+                    PIXEL_FORMAT_RGB_888, 0);
+
+            ASSERT_TRUE(mSurfaceControl != NULL);
+            ASSERT_TRUE(mSurfaceControl->isValid());
+
+            ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction());
+            ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000));
+            ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
+            ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction());
+
+            sp<ANativeWindow> window = mSurfaceControl->getSurface();
+            mEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
+                    window.get(), NULL);
+        } else {
+            EGLint pbufferAttribs[] = {
+                EGL_WIDTH, getSurfaceWidth(),
+                EGL_HEIGHT, getSurfaceHeight(),
+                EGL_NONE };
+
+            mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig,
+                    pbufferAttribs);
+        }
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+        mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT,
+                getContextAttribs());
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+                mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        EGLint w, h;
+        EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        RecordProperty("EglSurfaceWidth", w);
+        RecordProperty("EglSurfaceHeight", h);
+
+        glViewport(0, 0, w, h);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+    }
+
+    virtual void TearDown() {
+        // Display the result 
+        if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) {
+            eglSwapBuffers(mEglDisplay, mEglSurface);
+            sleep(mDisplaySecs);
+        }
+
+        if (mComposerClient != NULL) {
+            mComposerClient->dispose();
+        }
+        if (mEglContext != EGL_NO_CONTEXT) {
+            eglDestroyContext(mEglDisplay, mEglContext);
+        }
+        if (mEglSurface != EGL_NO_SURFACE) {
+            eglDestroySurface(mEglDisplay, mEglSurface);
+        }
+        if (mEglDisplay != EGL_NO_DISPLAY) {
+            eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+                    EGL_NO_CONTEXT);
+            eglTerminate(mEglDisplay);
+        }
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    }
+
+    virtual EGLint const* getConfigAttribs() {
+        static EGLint sDefaultConfigAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            EGL_ALPHA_SIZE, 8,
+            EGL_DEPTH_SIZE, 16,
+            EGL_STENCIL_SIZE, 8,
+            EGL_NONE };
+
+        return sDefaultConfigAttribs;
+    }
+
+    virtual EGLint const* getContextAttribs() {
+        static EGLint sDefaultContextAttribs[] = {
+            EGL_CONTEXT_CLIENT_VERSION, 2,
+            EGL_NONE };
+
+        return sDefaultContextAttribs;
+    }
+
+    virtual EGLint getSurfaceWidth() {
+        return 64;
+    }
+
+    virtual EGLint getSurfaceHeight() {
+        return 64;
+    }
+
+    void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
+        GLuint shader = glCreateShader(shaderType);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        if (shader) {
+            glShaderSource(shader, 1, &pSource, NULL);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            glCompileShader(shader);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            GLint compiled = 0;
+            glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            if (!compiled) {
+                GLint infoLen = 0;
+                glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+                ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+                if (infoLen) {
+                    char* buf = (char*) malloc(infoLen);
+                    if (buf) {
+                        glGetShaderInfoLog(shader, infoLen, NULL, buf);
+                        printf("Shader compile log:\n%s\n", buf);
+                        free(buf);
+                        FAIL();
+                    }
+                } else {
+                    char* buf = (char*) malloc(0x1000);
+                    if (buf) {
+                        glGetShaderInfoLog(shader, 0x1000, NULL, buf);
+                        printf("Shader compile log:\n%s\n", buf);
+                        free(buf);
+                        FAIL();
+                    }
+                }
+                glDeleteShader(shader);
+                shader = 0;
+            }
+        }
+        ASSERT_TRUE(shader != 0);
+        *outShader = shader;
+    }
+
+    void createProgram(const char* pVertexSource, const char* pFragmentSource,
+            GLuint* outPgm) {
+        GLuint vertexShader, fragmentShader;
+        {
+            SCOPED_TRACE("compiling vertex shader");
+            loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader);
+            if (HasFatalFailure()) {
+                return;
+            }
+        }
+        {
+            SCOPED_TRACE("compiling fragment shader");
+            loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader);
+            if (HasFatalFailure()) {
+                return;
+            }
+        }
+
+        GLuint program = glCreateProgram();
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        if (program) {
+            glAttachShader(program, vertexShader);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            glAttachShader(program, fragmentShader);
+            ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+            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);
+                        printf("Program link log:\n%s\n", buf);
+                        free(buf);
+                        FAIL();
+                    }
+                }
+                glDeleteProgram(program);
+                program = 0;
+            }
+        }
+        glDeleteShader(vertexShader);
+        glDeleteShader(fragmentShader);
+        ASSERT_TRUE(program != 0);
+        *outPgm = program;
+    }
+
+    ::testing::AssertionResult checkPixel(int x, int y, int r,
+            int g, int b, int a) {
+        GLubyte pixel[4];
+        String8 msg;
+        glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+        GLenum err = glGetError();
+        if (err != GL_NO_ERROR) {
+            msg += String8::format("error reading pixel: %#x", err);
+            while ((err = glGetError()) != GL_NO_ERROR) {
+                msg += String8::format(", %#x", err);
+            }
+            fprintf(stderr, "pixel check failure: %s\n", msg.string());
+            return ::testing::AssertionFailure(
+                    ::testing::Message(msg.string()));
+        }
+        if (r >= 0 && GLubyte(r) != pixel[0]) {
+            msg += String8::format("r(%d isn't %d)", pixel[0], r);
+        }
+        if (g >= 0 && GLubyte(g) != pixel[1]) {
+            if (!msg.isEmpty()) {
+                msg += " ";
+            }
+            msg += String8::format("g(%d isn't %d)", pixel[1], g);
+        }
+        if (b >= 0 && GLubyte(b) != pixel[2]) {
+            if (!msg.isEmpty()) {
+                msg += " ";
+            }
+            msg += String8::format("b(%d isn't %d)", pixel[2], b);
+        }
+        if (a >= 0 && GLubyte(a) != pixel[3]) {
+            if (!msg.isEmpty()) {
+                msg += " ";
+            }
+            msg += String8::format("a(%d isn't %d)", pixel[3], a);
+        }
+        if (!msg.isEmpty()) {
+            fprintf(stderr, "pixel check failure: %s\n", msg.string());
+            return ::testing::AssertionFailure(
+                    ::testing::Message(msg.string()));
+        } else {
+            return ::testing::AssertionSuccess();
+        }
+    }
+
+    int mDisplaySecs;
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<SurfaceControl> mSurfaceControl;
+
+    EGLDisplay mEglDisplay;
+    EGLSurface mEglSurface;
+    EGLContext mEglContext;
+};
+
+// XXX: Code above this point should live elsewhere
+
+class SurfaceTextureGLTest : public GLTest {
+protected:
+    static const GLint TEX_ID = 123;
+
+    virtual void SetUp() {
+        GLTest::SetUp();
+        mST = new SurfaceTexture(TEX_ID);
+        mSTC = new SurfaceTextureClient(mST);
+        mANW = mSTC;
+
+        const char vsrc[] =
+            "attribute vec4 vPosition;\n"
+            "varying vec2 texCoords;\n"
+            "uniform mat4 texMatrix;\n"
+            "void main() {\n"
+            "  vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n"
+            "  texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n"
+            "  gl_Position = vPosition;\n"
+            "}\n";
+
+        const char fsrc[] =
+            "#extension GL_OES_EGL_image_external : require\n"
+            "precision mediump float;\n"
+            "uniform samplerExternalOES texSampler;\n"
+            "varying vec2 texCoords;\n"
+            "void main() {\n"
+            "  gl_FragColor = texture2D(texSampler, texCoords);\n"
+            "}\n";
+
+        {
+            SCOPED_TRACE("creating shader program");
+            createProgram(vsrc, fsrc, &mPgm);
+            if (HasFatalFailure()) {
+                return;
+            }
+        }
+
+        mPositionHandle = glGetAttribLocation(mPgm, "vPosition");
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        ASSERT_NE(-1, mPositionHandle);
+        mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler");
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        ASSERT_NE(-1, mTexSamplerHandle);
+        mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix");
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        ASSERT_NE(-1, mTexMatrixHandle);
+    }
+
+    // drawTexture draws the SurfaceTexture over the entire GL viewport.
+    void drawTexture() {
+        const GLfloat triangleVertices[] = {
+            -1.0f, 1.0f,
+            -1.0f, -1.0f,
+            1.0f, -1.0f,
+            1.0f, 1.0f,
+        };
+
+        glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        glEnableVertexAttribArray(mPositionHandle);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+
+        glUseProgram(mPgm);
+        glUniform1i(mTexSamplerHandle, 0);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+        glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+
+        GLfloat texMatrix[16];
+        mST->getTransformMatrix(texMatrix);
+        glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix);
+
+        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+        ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+    }
+
+    sp<SurfaceTexture> mST;
+    sp<SurfaceTextureClient> mSTC;
+    sp<ANativeWindow> mANW;
+
+    GLuint mPgm;
+    GLint mPositionHandle;
+    GLint mTexSamplerHandle;
+    GLint mTexMatrixHandle;
+};
+
+// Fill a YV12 buffer with a multi-colored checkerboard pattern
+void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) {
+    const int blockWidth = w > 16 ? w / 16 : 1;
+    const int blockHeight = h > 16 ? h / 16 : 1;
+    const int yuvTexOffsetY = 0;
+    int yuvTexStrideY = stride;
+    int yuvTexOffsetV = yuvTexStrideY * h;
+    int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
+    int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
+    int yuvTexStrideU = yuvTexStrideV;
+    for (int x = 0; x < w; x++) {
+        for (int y = 0; y < h; y++) {
+            int parityX = (x / blockWidth) & 1;
+            int parityY = (y / blockHeight) & 1;
+            unsigned char intensity = (parityX ^ parityY) ? 63 : 191;
+            buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity;
+            if (x < w / 2 && y < h / 2) {
+                buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity;
+                if (x * 2 < w / 2 && y * 2 < h / 2) {
+                    buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] =
+                    buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] =
+                    buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] =
+                    buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] =
+                        intensity;
+                }
+            }
+        }
+    }
+}
+
+// Fill a YV12 buffer with red outside a given rectangle and green inside it.
+void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride,
+        const android_native_rect_t& rect) {
+    const int yuvTexOffsetY = 0;
+    int yuvTexStrideY = stride;
+    int yuvTexOffsetV = yuvTexStrideY * h;
+    int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
+    int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2;
+    int yuvTexStrideU = yuvTexStrideV;
+    for (int x = 0; x < w; x++) {
+        for (int y = 0; y < h; y++) {
+            bool inside = rect.left <= x && x < rect.right &&
+                    rect.top <= y && y < rect.bottom;
+            buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64;
+            if (x < w / 2 && y < h / 2) {
+                bool inside = rect.left <= 2*x && 2*x < rect.right &&
+                        rect.top <= 2*y && 2*y < rect.bottom;
+                buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16;
+                buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] =
+                        inside ? 16 : 255;
+            }
+        }
+    }
+}
+
+TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) {
+    const int yuvTexWidth = 64;
+    const int yuvTexHeight = 66;
+
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+            yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12));
+    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+    android_native_buffer_t* anb;
+    ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+    ASSERT_TRUE(anb != NULL);
+
+    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+    ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+
+    // Fill the buffer with the a checkerboard pattern
+    uint8_t* img = NULL;
+    buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+    fillYV12Buffer(img, yuvTexWidth, yuvTexHeight, buf->getStride());
+    buf->unlock();
+    ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
+
+    mST->updateTexImage();
+
+    glClearColor(0.2, 0.2, 0.2, 0.2);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    drawTexture();
+
+    EXPECT_TRUE(checkPixel( 0,  0, 255, 127, 255, 255));
+    EXPECT_TRUE(checkPixel(63,  0,   0, 133,   0, 255));
+    EXPECT_TRUE(checkPixel(63, 63,   0, 133,   0, 255));
+    EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255));
+
+    EXPECT_TRUE(checkPixel(22, 44, 247,  70, 255, 255));
+    EXPECT_TRUE(checkPixel(45, 52, 209,  32, 235, 255));
+    EXPECT_TRUE(checkPixel(52, 51, 100, 255,  73, 255));
+    EXPECT_TRUE(checkPixel( 7, 31, 155,   0, 118, 255));
+    EXPECT_TRUE(checkPixel(31,  9, 148,  71, 110, 255));
+    EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255));
+    EXPECT_TRUE(checkPixel(36, 22, 155,  29,   0, 255));
+}
+
+// XXX: This test is disabled because it it currently broken on all devices to
+// which I have access.  Some of the checkPixel calls are not correct because
+// I just copied them from the npot test above and haven't bothered to figure
+// out the correct values.
+TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledYV12BufferPow2) {
+    const int yuvTexWidth = 64;
+    const int yuvTexHeight = 64;
+
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+            yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12));
+    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+    android_native_buffer_t* anb;
+    ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+    ASSERT_TRUE(anb != NULL);
+
+    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+    ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+
+    // Fill the buffer with the a checkerboard pattern
+    uint8_t* img = NULL;
+    buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+    fillYV12Buffer(img, yuvTexWidth, yuvTexHeight, buf->getStride());
+    buf->unlock();
+    ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
+
+    mST->updateTexImage();
+
+    glClearColor(0.2, 0.2, 0.2, 0.2);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    drawTexture();
+
+    EXPECT_TRUE(checkPixel( 0,  0, 255, 127, 255, 255));
+    EXPECT_TRUE(checkPixel(63,  0,   0, 133,   0, 255));
+    EXPECT_TRUE(checkPixel(63, 63,   0, 133,   0, 255));
+    EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255));
+
+    EXPECT_TRUE(checkPixel(22, 19, 247,  70, 255, 255));
+    EXPECT_TRUE(checkPixel(45, 11, 209,  32, 235, 255));
+    EXPECT_TRUE(checkPixel(52, 12, 100, 255,  73, 255));
+    EXPECT_TRUE(checkPixel( 7, 32, 155,   0, 118, 255));
+    EXPECT_TRUE(checkPixel(31, 54, 148,  71, 110, 255));
+    EXPECT_TRUE(checkPixel(29, 28, 255, 127, 255, 255));
+    EXPECT_TRUE(checkPixel(36, 41, 155,  29,   0, 255));
+}
+
+TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) {
+    const int yuvTexWidth = 64;
+    const int yuvTexHeight = 66;
+
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+            yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12));
+    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+    android_native_rect_t crops[] = {
+        {4, 6, 22, 36},
+        {0, 6, 22, 36},
+        {4, 0, 22, 36},
+        {4, 6, yuvTexWidth, 36},
+        {4, 6, 22, yuvTexHeight},
+    };
+
+    for (int i = 0; i < 5; i++) {
+        const android_native_rect_t& crop(crops[i]);
+        SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left,
+                crop.top, crop.right, crop.bottom).string());
+
+        ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop));
+
+        android_native_buffer_t* anb;
+        ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+        ASSERT_TRUE(anb != NULL);
+
+        sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+        ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+
+        uint8_t* img = NULL;
+        buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+        fillYV12BufferRect(img, yuvTexWidth, yuvTexHeight, buf->getStride(), crop);
+        buf->unlock();
+        ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
+
+        mST->updateTexImage();
+
+        glClearColor(0.2, 0.2, 0.2, 0.2);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        drawTexture();
+
+        EXPECT_TRUE(checkPixel( 0,  0,  82, 255,  35, 255));
+        EXPECT_TRUE(checkPixel(63,  0,  82, 255,  35, 255));
+        EXPECT_TRUE(checkPixel(63, 63,  82, 255,  35, 255));
+        EXPECT_TRUE(checkPixel( 0, 63,  82, 255,  35, 255));
+
+        EXPECT_TRUE(checkPixel(25, 14,  82, 255,  35, 255));
+        EXPECT_TRUE(checkPixel(35, 31,  82, 255,  35, 255));
+        EXPECT_TRUE(checkPixel(57,  6,  82, 255,  35, 255));
+        EXPECT_TRUE(checkPixel( 5, 42,  82, 255,  35, 255));
+        EXPECT_TRUE(checkPixel(32, 33,  82, 255,  35, 255));
+        EXPECT_TRUE(checkPixel(16, 26,  82, 255,  35, 255));
+        EXPECT_TRUE(checkPixel(46, 51,  82, 255,  35, 255));
+    }
+}
+
+}
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index afabbf4..0dfbf01 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -712,11 +712,15 @@
     case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
         *value = MIN_UNDEQUEUED_BUFFERS;
         return NO_ERROR;
-    case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER:
+    case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
         sp<ISurfaceComposer> sf(ComposerService::getComposerService());
         *value = sf->authenticateSurface(mSurface) ? 1 : 0;
         return NO_ERROR;
     }
+    case NATIVE_WINDOW_CONCRETE_TYPE:
+        *value = NATIVE_WINDOW_SURFACE;
+        return NO_ERROR;
+    }
     return BAD_VALUE;
 }
 
@@ -749,6 +753,9 @@
     case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
         res = dispatch_set_buffers_transform( args );
         break;
+    case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
+        res = dispatch_set_buffers_timestamp( args );
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -788,6 +795,11 @@
     return setBuffersTransform(transform);
 }
 
+int Surface::dispatch_set_buffers_timestamp(va_list args) {
+    int64_t timestamp = va_arg(args, int64_t);
+    return setBuffersTimestamp(timestamp);
+}
+
 void Surface::setUsage(uint32_t reqUsage)
 {
     Mutex::Autolock _l(mSurfaceLock);
@@ -906,6 +918,13 @@
     return NO_ERROR;
 }
 
+int Surface::setBuffersTimestamp(int64_t timestamp)
+{
+    // Surface doesn't really have anything meaningful to do with timestamps
+    // so they'll just be dropped here.
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 int Surface::getConnectedApi() const
diff --git a/libs/surfaceflinger_client/tests/Surface_test.cpp b/libs/surfaceflinger_client/tests/Surface_test.cpp
index 74ebf4e..fd07479 100644
--- a/libs/surfaceflinger_client/tests/Surface_test.cpp
+++ b/libs/surfaceflinger_client/tests/Surface_test.cpp
@@ -130,4 +130,12 @@
     ASSERT_TRUE(heap != NULL);
 }
 
+TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
+    sp<ANativeWindow> anw(mSurface);
+    int result = -123;
+    int err = anw->query(anw.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result);
+    EXPECT_EQ(NO_ERROR, err);
+    EXPECT_EQ(NATIVE_WINDOW_SURFACE, result);
+}
+
 }
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 0702d49..dc223f9 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -286,6 +286,9 @@
         case NATIVE_WINDOW_FORMAT:
             *value = fb->format;
             return NO_ERROR;
+        case NATIVE_WINDOW_CONCRETE_TYPE:
+            *value = NATIVE_WINDOW_FRAMEBUFFER;
+            return NO_ERROR;
     }
     *value = 0;
     return BAD_VALUE;
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index eaa8926..440ec00 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -469,6 +469,16 @@
     return value;
 }
 
+ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
+    size_t pointerCount = mPointerIds.size();
+    for (size_t i = 0; i < pointerCount; i++) {
+        if (mPointerIds.itemAt(i) == pointerId) {
+            return i;
+        }
+    }
+    return -1;
+}
+
 void MotionEvent::offsetLocation(float xOffset, float yOffset) {
     mXOffset += xOffset;
     mYOffset += yOffset;
@@ -668,12 +678,27 @@
 void VelocityTracker::clear() {
     mIndex = 0;
     mMovements[0].idBits.clear();
+    mActivePointerId = -1;
+}
+
+void VelocityTracker::clearPointers(BitSet32 idBits) {
+    BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
+    mMovements[mIndex].idBits = remainingIdBits;
+
+    if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) {
+        mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
+    }
 }
 
 void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) {
     if (++mIndex == HISTORY_SIZE) {
         mIndex = 0;
     }
+
+    while (idBits.count() > MAX_POINTERS) {
+        idBits.clearBit(idBits.lastMarkedBit());
+    }
+
     Movement& movement = mMovements[mIndex];
     movement.eventTime = eventTime;
     movement.idBits = idBits;
@@ -682,8 +707,13 @@
         movement.positions[i] = positions[i];
     }
 
+    if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) {
+        mActivePointerId = count != 0 ? idBits.firstMarkedBit() : -1;
+    }
+
 #if DEBUG_VELOCITY
-    LOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x", eventTime, idBits.value);
+    LOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d",
+            eventTime, idBits.value, mActivePointerId);
     for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
         uint32_t id = iterBits.firstMarkedBit();
         uint32_t index = idBits.getIndexOfBit(id);
@@ -691,7 +721,7 @@
         float vx, vy;
         bool available = getVelocity(id, &vx, &vy);
         if (available) {
-            LOGD("  %d: position (%0.3f, %0.3f), velocity (%0.3f, %0.3f), speed %0.3f",
+            LOGD("  %d: position (%0.3f, %0.3f), vx=%0.3f, vy=%0.3f, speed=%0.3f",
                     id, positions[index].x, positions[index].y, vx, vy, sqrtf(vx * vx + vy * vy));
         } else {
             assert(vx == 0 && vy == 0);
@@ -702,6 +732,70 @@
 #endif
 }
 
+void VelocityTracker::addMovement(const MotionEvent* event) {
+    int32_t actionMasked = event->getActionMasked();
+
+    switch (actionMasked) {
+    case AMOTION_EVENT_ACTION_DOWN:
+        // Clear all pointers on down before adding the new movement.
+        clear();
+        break;
+    case AMOTION_EVENT_ACTION_POINTER_DOWN: {
+        // Start a new movement trace for a pointer that just went down.
+        // We do this on down instead of on up because the client may want to query the
+        // final velocity for a pointer that just went up.
+        BitSet32 downIdBits;
+        downIdBits.markBit(event->getActionIndex());
+        clearPointers(downIdBits);
+        break;
+    }
+    case AMOTION_EVENT_ACTION_OUTSIDE:
+    case AMOTION_EVENT_ACTION_CANCEL:
+    case AMOTION_EVENT_ACTION_SCROLL:
+    case AMOTION_EVENT_ACTION_UP:
+    case AMOTION_EVENT_ACTION_POINTER_UP:
+        // Ignore these actions because they do not convey any new information about
+        // pointer movement.  We also want to preserve the last known velocity of the pointers.
+        // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position
+        // of the pointers that went up.  ACTION_POINTER_UP does include the new position of
+        // pointers that remained down but we will also receive an ACTION_MOVE with this
+        // information if any of them actually moved.  Since we don't know how many pointers
+        // will be going up at once it makes sense to just wait for the following ACTION_MOVE
+        // before adding the movement.
+        return;
+    }
+
+    size_t pointerCount = event->getPointerCount();
+    if (pointerCount > MAX_POINTERS) {
+        pointerCount = MAX_POINTERS;
+    }
+
+    BitSet32 idBits;
+    for (size_t i = 0; i < pointerCount; i++) {
+        idBits.markBit(event->getPointerId(i));
+    }
+
+    nsecs_t eventTime;
+    Position positions[pointerCount];
+
+    size_t historySize = event->getHistorySize();
+    for (size_t h = 0; h < historySize; h++) {
+        eventTime = event->getHistoricalEventTime(h);
+        for (size_t i = 0; i < pointerCount; i++) {
+            positions[i].x = event->getHistoricalX(i, h);
+            positions[i].y = event->getHistoricalY(i, h);
+        }
+        addMovement(eventTime, idBits, positions);
+    }
+
+    eventTime = event->getEventTime();
+    for (size_t i = 0; i < pointerCount; i++) {
+        positions[i].x = event->getX(i);
+        positions[i].y = event->getY(i);
+    }
+    addMovement(eventTime, idBits, positions);
+}
+
 bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
     const Movement& newestMovement = mMovements[mIndex];
     if (newestMovement.idBits.hasBit(id)) {
@@ -719,36 +813,17 @@
             oldestIndex = nextOldestIndex;
         } while (++numTouches < HISTORY_SIZE);
 
-        // If we have a lot of samples, skip the last received sample since it is
-        // probably pretty noisy compared to the sum of all of the traces already acquired.
+        // Calculate an exponentially weighted moving average of the velocity estimate
+        // at different points in time measured relative to the oldest sample.
+        // This is essentially an IIR filter.  Newer samples are weighted more heavily
+        // than older samples.  Samples at equal time points are weighted more or less
+        // equally.
         //
-        // NOTE: This condition exists in the android.view.VelocityTracker and imposes a
-        // bias against the most recent data.
-        if (numTouches > 3) {
-            numTouches -= 1;
-        }
-
-        // Calculate an exponentially weighted moving average of the velocity at different
-        // points in time measured relative to the oldest samples.  This is essentially
-        // an IIR filter.
-        //
-        // One problem with this algorithm is that the sample data may be poorly conditioned.
+        // One tricky problem is that the sample data may be poorly conditioned.
         // Sometimes samples arrive very close together in time which can cause us to
         // overestimate the velocity at that time point.  Most samples might be measured
-        // 16ms apart but some consecutive samples could be only 0.5sm apart due to
-        // the way they are reported by the hardware or driver (sometimes in bursts or with
-        // significant jitter).  The instantaneous velocity for those samples 0.5ms apart will
-        // be calculated to be 32 times what it should have been.
-        // To work around this effect, we impose a minimum duration on the samples.
-        //
-        // FIXME: Samples close together in time can have an disproportionately large
-        // impact on the result because all samples are equally weighted.  The average should
-        // instead take the time factor into account.
-        //
-        // FIXME: The minimum duration condition does not exist in
-        // android.view.VelocityTracker yet.  It is less important there because sample times
-        // are truncated to the millisecond so back to back samples will often appear to be
-        // zero milliseconds apart and will be ignored if they are the oldest ones.
+        // 16ms apart but some consecutive samples could be only 0.5sm apart because
+        // the hardware or driver reports them irregularly or in bursts.
         float accumVx = 0;
         float accumVy = 0;
         uint32_t index = oldestIndex;
@@ -756,19 +831,27 @@
         const Movement& oldestMovement = mMovements[oldestIndex];
         const Position& oldestPosition =
                 oldestMovement.positions[oldestMovement.idBits.getIndexOfBit(id)];
+        nsecs_t lastDuration = 0;
         while (numTouches-- > 1) {
             if (++index == HISTORY_SIZE) {
                 index = 0;
             }
             const Movement& movement = mMovements[index];
             nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
-            if (duration > MIN_DURATION) {
+
+            // If the duration between samples is small, we may significantly overestimate
+            // the velocity.  Consequently, we impose a minimum duration constraint on the
+            // samples that we include in the calculation.
+            if (duration >= MIN_DURATION) {
                 const Position& position = movement.positions[movement.idBits.getIndexOfBit(id)];
                 float scale = 1000000000.0f / duration; // one over time delta in seconds
                 float vx = (position.x - oldestPosition.x) * scale;
                 float vy = (position.y - oldestPosition.y) * scale;
-                accumVx = accumVx == 0 ? vx : (accumVx + vx) * 0.5f;
-                accumVy = accumVy == 0 ? vy : (accumVy + vy) * 0.5f;
+
+                accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
+                accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
+
+                lastDuration = duration;
                 samplesUsed += 1;
             }
         }
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 1994f6a..a060a5f 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -56,6 +56,9 @@
 Region::Region(const Region& rhs)
     : mBounds(rhs.mBounds), mStorage(rhs.mStorage)
 {
+#if VALIDATE_REGIONS
+    validate(rhs, "rhs copy-ctor");
+#endif
 }
 
 Region::Region(const Rect& rhs)
@@ -76,7 +79,8 @@
 Region& Region::operator = (const Region& rhs)
 {
 #if VALIDATE_REGIONS
-    validate(rhs, "operator=");
+    validate(*this, "this->operator=");
+    validate(rhs, "rhs.operator=");
 #endif
     mBounds = rhs.mBounds;
     mStorage = rhs.mStorage;
@@ -366,6 +370,12 @@
         const Region& lhs,
         const Region& rhs, int dx, int dy)
 {
+#if VALIDATE_REGIONS
+    validate(lhs, "boolean_operation (before): lhs");
+    validate(rhs, "boolean_operation (before): rhs");
+    validate(dst, "boolean_operation (before): dst");
+#endif
+
     size_t lhs_count;
     Rect const * const lhs_rects = lhs.getArray(&lhs_count);
 
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index 18f858b..d5dd126 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -218,14 +218,10 @@
     // Adjust the timeout based on when the next message is due.
     if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-        if (mNextMessageUptime <= now) {
-            timeoutMillis = 0;
-        } else {
-            uint64_t delay = (mNextMessageUptime - now + 999999LL) / 1000000LL;
-            if (delay < INT_MAX
-                    && (timeoutMillis < 0 || int(delay) < timeoutMillis)) {
-                timeoutMillis = int(delay);
-            }
+        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
+        if (messageTimeoutMillis >= 0
+                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
+            timeoutMillis = messageTimeoutMillis;
         }
 #if DEBUG_POLL_AND_WAKE
         LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
@@ -444,12 +440,11 @@
                 return result;
             }
 
-            nsecs_t timeoutNanos = endTime - systemTime(SYSTEM_TIME_MONOTONIC);
-            if (timeoutNanos <= 0) {
+            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+            timeoutMillis = toMillisecondTimeoutDelay(now, endTime);
+            if (timeoutMillis == 0) {
                 return ALOOPER_POLL_TIMEOUT;
             }
-
-            timeoutMillis = int(nanoseconds_to_milliseconds(timeoutNanos + 999999LL));
         }
     }
 }
diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp
index 784f035..64a29f5 100644
--- a/libs/utils/Timers.cpp
+++ b/libs/utils/Timers.cpp
@@ -26,6 +26,7 @@
 #include <sys/time.h>
 #include <time.h>
 #include <errno.h>
+#include <limits.h>
 
 #ifdef HAVE_WIN32_THREADS
 #include <windows.h>
@@ -53,6 +54,23 @@
 #endif
 }
 
+int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime)
+{
+    int timeoutDelayMillis;
+    if (timeoutTime > referenceTime) {
+        uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
+        if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) {
+            timeoutDelayMillis = -1;
+        } else {
+            timeoutDelayMillis = (timeoutDelay + 999999LL) / 1000000LL;
+        }
+    } else {
+        timeoutDelayMillis = 0;
+    }
+    return timeoutDelayMillis;
+}
+
+
 /*
  * ===========================================================================
  *      DurationTimer
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 861d7ac..75f7078 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -340,7 +340,10 @@
     }
     
     if (gEGLDebugLevel > 0)
-        StartDebugServer();
+    {
+        property_get("debug.egl.debug_port", value, "5039");
+        StartDebugServer(atoi(value));
+    }
 }
 
 static void setGLHooksThreadSpecific(gl_hooks_t const *value) {
@@ -350,7 +353,6 @@
     } else if (gEGLDebugLevel > 0 && value != &gHooksNoContext) {
         setGlTraceThreadSpecific(value);
         setGlThreadSpecific(&gHooksDebug);
-        LOGD("\n* setGLHooksThreadSpecific gHooksDebug");
     } else {
         setGlThreadSpecific(value);
     }
@@ -1130,6 +1132,16 @@
         EGLConfig iConfig = dp->configs[intptr_t(config)].config;
         EGLint format;
 
+        // for now fail if the window is not a Surface.
+        int type = -1;
+        ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
+        if ((anw->query(window, NATIVE_WINDOW_CONCRETE_TYPE, &type) != 0) ||
+                (type == NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT)) {
+            LOGE("native window is a SurfaceTextureClient (currently "
+                    "unsupported)");
+            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+        }
+
         // set the native window's buffers format to match this config
         if (cnx->egl.eglGetConfigAttrib(iDpy,
                 iConfig, EGL_NATIVE_VISUAL_ID, &format)) {
@@ -2109,14 +2121,15 @@
     if (!validate_display_context(dpy, ctx))
         return EGL_FALSE;
 
+    EGLBoolean result = EGL_FALSE;
     egl_context_t * const c = get_context(ctx);
-
     if (c->cnx->egl.eglDestroySyncKHR) {
-        return c->cnx->egl.eglDestroySyncKHR(
+        result = c->cnx->egl.eglDestroySyncKHR(
                 dp->disp[c->impl].dpy, syncObject->sync);
+        if (result)
+            _s.terminate();
     }
-
-    return EGL_FALSE;
+    return result;
 }
 
 EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout)
diff --git a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
index b14885b..48a29da 100755
--- a/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
+++ b/opengl/libs/GLES2_dbg/generate_debugger_message_proto.py
@@ -98,9 +98,6 @@
     output.write("        SETPROP = %d;\n" % (i))
     i += 1
     
-    output.write("        CAPTURE = %d;\n" % (i))
-    i += 1
-
     output.write("""    }
     required Function function = 2 [default = NEG]; // type/function of message
     enum Type
diff --git a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
index 68514df..5c41825 100644
--- a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
+++ b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
@@ -65,8 +65,9 @@
 
 void DbgContext::glUseProgram(GLuint program)
 {
-    assert(GL_NO_ERROR == hooks->gl.glGetError());
-
+    while (GLenum error = hooks->gl.glGetError())
+        LOGD("DbgContext::glUseProgram: before glGetError() = 0x%.4X", error);
+        
     this->program = program;
 
     GLint activeAttributes = 0;
@@ -106,6 +107,9 @@
             maxAttrib = slot;
     }
     delete name;
+    
+    while (GLenum error = hooks->gl.glGetError())
+        LOGD("DbgContext::glUseProgram: after glGetError() = 0x%.4X", error);
 }
 
 static bool HasNonVBOAttribs(const DbgContext * const ctx)
diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
index 4083c44..1f404c2 100644
--- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
+++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.cpp
@@ -229,7 +229,6 @@
     case 188:
     case 189:
     case 190:
-    case 191:
       return true;
     default:
       return false;
@@ -428,7 +427,6 @@
 const Message_Function Message::CONTINUE;
 const Message_Function Message::SKIP;
 const Message_Function Message::SETPROP;
-const Message_Function Message::CAPTURE;
 const Message_Function Message::Function_MIN;
 const Message_Function Message::Function_MAX;
 const int Message::Function_ARRAYSIZE;
diff --git a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
index 3f2b842..59e7bab 100644
--- a/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
+++ b/opengl/libs/GLES2_dbg/src/debugger_message.pb.h
@@ -226,12 +226,11 @@
   Message_Function_NEG = 187,
   Message_Function_CONTINUE = 188,
   Message_Function_SKIP = 189,
-  Message_Function_SETPROP = 190,
-  Message_Function_CAPTURE = 191
+  Message_Function_SETPROP = 190
 };
 bool Message_Function_IsValid(int value);
 const Message_Function Message_Function_Function_MIN = Message_Function_glActiveTexture;
-const Message_Function Message_Function_Function_MAX = Message_Function_CAPTURE;
+const Message_Function Message_Function_Function_MAX = Message_Function_SETPROP;
 const int Message_Function_Function_ARRAYSIZE = Message_Function_Function_MAX + 1;
 
 enum Message_Type {
@@ -488,7 +487,6 @@
   static const Function CONTINUE = Message_Function_CONTINUE;
   static const Function SKIP = Message_Function_SKIP;
   static const Function SETPROP = Message_Function_SETPROP;
-  static const Function CAPTURE = Message_Function_CAPTURE;
   static inline bool Function_IsValid(int value) {
     return Message_Function_IsValid(value);
   }
diff --git a/opengl/libs/GLES2_dbg/src/egl.cpp b/opengl/libs/GLES2_dbg/src/egl.cpp
index b3979a3..27c7f7e 100644
--- a/opengl/libs/GLES2_dbg/src/egl.cpp
+++ b/opengl/libs/GLES2_dbg/src/egl.cpp
@@ -19,7 +19,7 @@
 EGLBoolean Debug_eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
 {
     glesv2debugger::Message msg;
-    const bool expectResponse = true;
+    const bool expectResponse = false;
     struct : public FunctionCall {
         EGLDisplay dpy;
         EGLSurface draw;
diff --git a/opengl/libs/GLES2_dbg/src/header.h b/opengl/libs/GLES2_dbg/src/header.h
index cbd448a..b79cc0f 100644
--- a/opengl/libs/GLES2_dbg/src/header.h
+++ b/opengl/libs/GLES2_dbg/src/header.h
@@ -113,7 +113,7 @@
     virtual ~FunctionCall() {}
 };
 
-// move these into DbgContext
+// move these into DbgContext as static
 extern bool capture;
 extern int timeMode; // SYSTEM_TIME_
 
@@ -121,8 +121,10 @@
 
 unsigned GetBytesPerPixel(const GLenum format, const GLenum type);
 
+// every Debug_gl* function calls this to send message to client and possibly receive commands
 int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg,
                   const bool expectResponse, const glesv2debugger::Message_Function function);
+
 void Receive(glesv2debugger::Message & cmd);
 float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd);
 void SetProp(const glesv2debugger::Message & cmd);
diff --git a/opengl/libs/GLES2_dbg/src/server.cpp b/opengl/libs/GLES2_dbg/src/server.cpp
index 820e9de..03c3dae 100644
--- a/opengl/libs/GLES2_dbg/src/server.cpp
+++ b/opengl/libs/GLES2_dbg/src/server.cpp
@@ -38,7 +38,7 @@
     exit(1);
 }
 
-void StartDebugServer()
+void StartDebugServer(unsigned short port)
 {
     LOGD("GLESv2_dbg: StartDebugServer");
     if (serverSock >= 0)
@@ -53,8 +53,8 @@
     }
     /* Construct the server sockaddr_in structure */
     server.sin_family = AF_INET;                  /* Internet/IP */
-    server.sin_addr.s_addr = htonl(INADDR_ANY);   /* Incoming addr */
-    server.sin_port = htons(5039);       /* server port */
+    server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);   /* Incoming addr */
+    server.sin_port = htons(port);       /* server port */
 
     /* Bind the server socket */
     socklen_t sizeofSockaddr_in = sizeof(sockaddr_in);
@@ -79,13 +79,6 @@
 
     LOGD("Client connected: %s\n", inet_ntoa(client.sin_addr));
 //    fcntl(clientSock, F_SETFL, O_NONBLOCK);
-
-    glesv2debugger::Message msg, cmd;
-    msg.set_context_id(0);
-    msg.set_function(glesv2debugger::Message_Function_ACK);
-    msg.set_type(glesv2debugger::Message_Type_Response);
-    msg.set_expect_response(false);
-    Send(msg, cmd);
 }
 
 void StopDebugServer()
@@ -130,6 +123,27 @@
     cmd.ParseFromArray(buffer, len);
 }
 
+bool TryReceive(glesv2debugger::Message & cmd)
+{
+    fd_set readSet;
+    FD_ZERO(&readSet);
+    FD_SET(clientSock, &readSet);
+    timeval timeout;
+    timeout.tv_sec = timeout.tv_usec = 0;
+
+    int rc = select(clientSock + 1, &readSet, NULL, NULL, &timeout);
+    if (rc < 0)
+        Die("failed to select clientSock");
+
+    bool received = false;
+    if (FD_ISSET(clientSock, &readSet)) {
+        LOGD("TryReceive: avaiable for read");
+        Receive(cmd);
+        return true;
+    }
+    return false;
+}
+
 float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd)
 {
     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -155,12 +169,18 @@
         Die("Failed to send message");
     }
 
+    // try to receive commands even though not expecting response,
+    // since client can send SETPROP commands anytime
     if (!msg.expect_response()) {
-        pthread_mutex_unlock(&mutex);
-        return t;
-    }
-
-    Receive(cmd);
+        if (TryReceive(cmd)) {
+            LOGD("Send: TryReceived");
+            if (glesv2debugger::Message_Function_SETPROP == cmd.function())
+                LOGD("Send: received SETPROP");
+            else
+                LOGD("Send: received something else");
+        }
+    } else
+        Receive(cmd);
 
     //LOGD("Message sent tid=%lu len=%d", pthread_self(), str.length());
     pthread_mutex_unlock(&mutex);
@@ -193,24 +213,26 @@
     msg.set_type(glesv2debugger::Message_Type_BeforeCall);
     msg.set_expect_response(expectResponse);
     msg.set_function(function);
-    Send(msg, cmd);
     if (!expectResponse)
         cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    Send(msg, cmd);
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
         switch (cmd.function()) {
         case glesv2debugger::Message_Function_CONTINUE:
             ret = functionCall(&dbg->hooks->gl, msg);
+            while (GLenum error = dbg->hooks->gl.glGetError())
+                LOGD("Function=%u glGetError() = 0x%.4X", function, error);
             if (!msg.has_time()) // some has output data copy, so time inside call
                 msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
             msg.set_context_id(reinterpret_cast<int>(dbg));
             msg.set_function(function);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             msg.set_expect_response(expectResponse);
-            Send(msg, cmd);
             if (!expectResponse)
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+            Send(msg, cmd);
             break;
         case glesv2debugger::Message_Function_SKIP:
             return const_cast<int *>(ret);
diff --git a/opengl/libs/GLES2_dbg/src/vertex.cpp b/opengl/libs/GLES2_dbg/src/vertex.cpp
index 52ce907..a73967f 100644
--- a/opengl/libs/GLES2_dbg/src/vertex.cpp
+++ b/opengl/libs/GLES2_dbg/src/vertex.cpp
@@ -41,10 +41,10 @@
     msg.set_arg6(reinterpret_cast<int>(pixels));
     //void * data = NULL;
     //unsigned encodedSize = 0;
-    Send(msg, cmd);
-    float t = 0;
     if (!expectResponse)
         cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    Send(msg, cmd);
+    float t = 0;
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -61,6 +61,8 @@
             //msg.set_data(data, encodedSize);
             //free(data);
             c0 = systemTime(timeMode);
+            if (!expectResponse)
+                cmd.set_function(glesv2debugger::Message_Function_SKIP);
             t = Send(msg, cmd);
             msg.set_time((systemTime(timeMode) - c0) * 1e-6f);
             msg.set_clock(t);
@@ -69,11 +71,13 @@
             msg.set_expect_response(false);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             //Send(msg, cmd);
-            if (!expectResponse)
-                cmd.set_function(glesv2debugger::Message_Function_SKIP);
             break;
         case glesv2debugger::Message_Function_SKIP:
             return;
+        case glesv2debugger::Message_Function_SETPROP:
+            SetProp(cmd);
+            Receive(cmd);
+            break;
         default:
             assert(0); //GenerateCall(msg, cmd);
             break;
@@ -104,9 +108,9 @@
     void * pixels = NULL;
     GLint readFormat = 0, readType = 0;
     int viewport[4] = {};
-    Send(msg, cmd);
     if (!expectResponse)
         cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    Send(msg, cmd);
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -118,25 +122,26 @@
             msg.set_function(glesv2debugger::Message_Function_glDrawArrays);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             msg.set_expect_response(expectResponse);
-            Send(msg, cmd);
-            if (capture)
-                cmd.set_function(glesv2debugger::Message_Function_CAPTURE);
-            else if (!expectResponse)
+            if (!expectResponse)
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+            Send(msg, cmd);
+            if (capture) {
+                dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
+                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
+                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
+                LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
+                     viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
+                pixels = malloc(viewport[2] * viewport[3] * 4);
+                Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
+                                   readFormat, readType, pixels);
+                free(pixels);
+            }
             break;
         case glesv2debugger::Message_Function_SKIP:
             return;
-        case glesv2debugger::Message_Function_CAPTURE:
-            dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
-            dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
-            dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
-            LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
-                 viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
-            pixels = malloc(viewport[2] * viewport[3] * 4);
-            Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
-                               readFormat, readType, pixels);
-            free(pixels);
-            cmd.set_function(glesv2debugger::Message_Function_SKIP);
+        case glesv2debugger::Message_Function_SETPROP:
+            SetProp(cmd);
+            Receive(cmd);
             break;
         default:
             assert(0); //GenerateCall(msg, cmd);
@@ -189,9 +194,9 @@
     void * pixels = NULL;
     GLint readFormat = 0, readType = 0;
     int viewport[4] = {};
-    Send(msg, cmd);
     if (!expectResponse)
         cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+    Send(msg, cmd);
     while (true) {
         msg.Clear();
         nsecs_t c0 = systemTime(timeMode);
@@ -203,25 +208,26 @@
             msg.set_function(glesv2debugger::Message_Function_glDrawElements);
             msg.set_type(glesv2debugger::Message_Type_AfterCall);
             msg.set_expect_response(expectResponse);
-            Send(msg, cmd);
-            if (capture)
-                cmd.set_function(glesv2debugger::Message_Function_CAPTURE);
-            else if (!expectResponse)
+            if (!expectResponse)
                 cmd.set_function(glesv2debugger::Message_Function_SKIP);
+            Send(msg, cmd);
+            if (capture) {
+                dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
+                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
+                dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
+                LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
+                     viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
+                pixels = malloc(viewport[2] * viewport[3] * 4);
+                Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
+                                   readFormat, readType, pixels);
+                free(pixels);
+            }
             break;
         case glesv2debugger::Message_Function_SKIP:
             return;
-        case glesv2debugger::Message_Function_CAPTURE:
-            dbg->hooks->gl.glGetIntegerv(GL_VIEWPORT, viewport);
-            dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
-            dbg->hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
-            LOGD("glDrawArrays CAPTURE: x=%d y=%d width=%d height=%d format=0x%.4X type=0x%.4X",
-                 viewport[0], viewport[1], viewport[2], viewport[3], readFormat, readType);
-            pixels = malloc(viewport[2] * viewport[3] * 4);
-            Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
-                               readFormat, readType, pixels);
-            free(pixels);
-            cmd.set_function(glesv2debugger::Message_Function_SKIP);
+        case glesv2debugger::Message_Function_SETPROP:
+            SetProp(cmd);
+            Receive(cmd);
             break;
         default:
             assert(0); //GenerateCall(msg, cmd);
diff --git a/opengl/libs/glesv2dbg.h b/opengl/libs/glesv2dbg.h
index b988eb78..8029dce 100644
--- a/opengl/libs/glesv2dbg.h
+++ b/opengl/libs/glesv2dbg.h
@@ -24,7 +24,7 @@
     DbgContext * CreateDbgContext(const unsigned version, const gl_hooks_t * const hooks);
     void DestroyDbgContext(DbgContext * const dbg);
     
-    void StartDebugServer(); // create and bind socket if haven't already
+    void StartDebugServer(unsigned short port); // create and bind socket if haven't already
     void StopDebugServer(); // close socket if open
     
 }; // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 7a69097..c4027e0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -811,7 +811,7 @@
 
     { // scope for strong mUserClient reference
         sp<UserClient> userClient(mUserClient.promote());
-        if (mUserClient != 0 && mControlBlock != 0) {
+        if (userClient != 0 && mControlBlock != 0) {
             mControlBlock->setStatus(NO_INIT);
         }
     }
@@ -860,11 +860,13 @@
     Mutex::Autolock _l(mLock);
 
     if (size < mNumBuffers) {
-        // Move the active texture into slot 0
-        BufferData activeBufferData = mBufferData[mActiveBufferIndex];
-        mBufferData[mActiveBufferIndex] = mBufferData[0];
-        mBufferData[0] = activeBufferData;
-        mActiveBufferIndex = 0;
+        // If there is an active texture, move it into slot 0 if needed
+        if (mActiveBufferIndex > 0) {
+            BufferData activeBufferData = mBufferData[mActiveBufferIndex];
+            mBufferData[mActiveBufferIndex] = mBufferData[0];
+            mBufferData[0] = activeBufferData;
+            mActiveBufferIndex = 0;
+        }
 
         // Free the buffers that are no longer needed.
         for (size_t i = size; i < mNumBuffers; i++) {
diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp
index 99f4b4f..0ccca77 100644
--- a/services/surfaceflinger/tests/resize/resize.cpp
+++ b/services/surfaceflinger/tests/resize/resize.cpp
@@ -1,3 +1,19 @@
+/*
+ * 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 <cutils/memory.h>
 
 #include <utils/Log.h>
diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp
index 194fbb6..67ecf7e 100644
--- a/services/surfaceflinger/tests/surface/surface.cpp
+++ b/services/surfaceflinger/tests/surface/surface.cpp
@@ -1,3 +1,19 @@
+/*
+ * 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 <cutils/memory.h>
 
 #include <utils/Log.h>