Merge "MediaScanner: To query row ids of music files when handling playlists."
diff --git a/include/ui/Input.h b/include/ui/Input.h
index f1385a0..438a1a0 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -620,10 +620,41 @@
  */
 class VelocityTracker {
 public:
+    // Default polynomial degree.  (used by getVelocity)
+    static const uint32_t DEFAULT_DEGREE = 2;
+
+    // Default sample horizon.  (used by getVelocity)
+    // We don't use too much history by default since we want to react to quick
+    // changes in direction.
+    static const nsecs_t DEFAULT_HORIZON = 100 * 1000000; // 100 ms
+
     struct Position {
         float x, y;
     };
 
+    struct Estimator {
+        static const size_t MAX_DEGREE = 2;
+
+        // Polynomial coefficients describing motion in X and Y.
+        float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1];
+
+        // Polynomial degree (number of coefficients), or zero if no information is
+        // available.
+        uint32_t degree;
+
+        // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
+        float confidence;
+
+        inline void clear() {
+            degree = 0;
+            confidence = 0;
+            for (size_t i = 0; i <= MAX_DEGREE; i++) {
+                xCoeff[i] = 0;
+                yCoeff[i] = 0;
+            }
+        }
+    };
+
     VelocityTracker();
 
     // Resets the velocity tracker state.
@@ -645,10 +676,16 @@
     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.
+    // Returns false and sets the velocity components to zero if there is
+    // insufficient movement information for the pointer.
     bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
 
+    // Gets a quadratic estimator for the movements of the specified pointer id.
+    // Returns false and clears the estimator if there is no information available
+    // about the pointer.
+    bool getEstimator(uint32_t id, uint32_t degree, nsecs_t horizon,
+            Estimator* outEstimator) const;
+
     // Gets the active pointer id, or -1 if none.
     inline int32_t getActivePointerId() const { return mActivePointerId; }
 
@@ -657,18 +694,16 @@
 
 private:
     // Number of samples to keep.
-    static const uint32_t HISTORY_SIZE = 10;
-
-    // Oldest sample to consider when calculating the velocity.
-    static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms
-
-    // The minimum duration between samples when estimating velocity.
-    static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
+    static const uint32_t HISTORY_SIZE = 20;
 
     struct Movement {
         nsecs_t eventTime;
         BitSet32 idBits;
         Position positions[MAX_POINTERS];
+
+        inline const Position& getPosition(uint32_t id) const {
+            return positions[idBits.getIndexOfBit(id)];
+        }
     };
 
     uint32_t mIndex;
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index 4e65a2d..9e98bc5 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -27,7 +27,7 @@
 class Rect : public ARect
 {
 public:
-    typedef int32_t value_type;
+    typedef ARect::value_type value_type;
 
     // we don't provide copy-ctor and operator= on purpose
     // because we want the compiler generated versions
diff --git a/include/utils/threads.h b/include/utils/threads.h
index c685625..ab3e8cd 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -143,6 +143,10 @@
 // in either case errno is set.  Thread ID zero means current thread.
 extern int androidSetThreadPriority(pid_t tid, int prio);
 
+// Get the current priority of a particular thread. Returns one of the
+// ANDROID_PRIORITY constants or a negative result in case of error.
+extern int androidGetThreadPriority(pid_t tid);
+
 // Get the current scheduling group of a particular thread. Normally returns
 // one of the ANDROID_TGROUP constants other than ANDROID_TGROUP_DEFAULT.
 // Returns ANDROID_TGROUP_DEFAULT if no pthread support (e.g. on host) or if
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 392193b..5ccf87f 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -78,12 +78,8 @@
 // conditionals don't get stripped...  but that is probably what we want.
 #if !LOG_NDEBUG
 static const char *kReturnStrings[] = {
-#if 1 /* TODO: error update strings */
-    "unknown",
-#else
+    "BR_ERROR",
     "BR_OK",
-    "BR_TIMEOUT",
-    "BR_WAKEUP",
     "BR_TRANSACTION",
     "BR_REPLY",
     "BR_ACQUIRE_RESULT",
@@ -94,25 +90,19 @@
     "BR_RELEASE",
     "BR_DECREFS",
     "BR_ATTEMPT_ACQUIRE",
-    "BR_EVENT_OCCURRED",
     "BR_NOOP",
     "BR_SPAWN_LOOPER",
     "BR_FINISHED",
     "BR_DEAD_BINDER",
-    "BR_CLEAR_DEATH_NOTIFICATION_DONE"
-#endif
+    "BR_CLEAR_DEATH_NOTIFICATION_DONE",
+    "BR_FAILED_REPLY"
 };
 
 static const char *kCommandStrings[] = {
-#if 1 /* TODO: error update strings */
-    "unknown",
-#else
-    "BC_NOOP",
     "BC_TRANSACTION",
     "BC_REPLY",
     "BC_ACQUIRE_RESULT",
     "BC_FREE_BUFFER",
-    "BC_TRANSACTION_COMPLETE",
     "BC_INCREFS",
     "BC_ACQUIRE",
     "BC_RELEASE",
@@ -120,18 +110,12 @@
     "BC_INCREFS_DONE",
     "BC_ACQUIRE_DONE",
     "BC_ATTEMPT_ACQUIRE",
-    "BC_RETRIEVE_ROOT_OBJECT",
-    "BC_SET_THREAD_ENTRY",
     "BC_REGISTER_LOOPER",
     "BC_ENTER_LOOPER",
     "BC_EXIT_LOOPER",
-    "BC_SYNC",
-    "BC_STOP_PROCESS",
-    "BC_STOP_SELF",
     "BC_REQUEST_DEATH_NOTIFICATION",
     "BC_CLEAR_DEATH_NOTIFICATION",
     "BC_DEAD_BINDER_DONE"
-#endif
 };
 
 static const char* getReturnString(size_t idx)
@@ -154,30 +138,36 @@
 {
     const binder_transaction_data* btd =
         (const binder_transaction_data*)data;
-    out << "target=" << btd->target.ptr << " (cookie " << btd->cookie << ")" << endl
+    if (btd->target.handle < 1024) {
+        /* want to print descriptors in decimal; guess based on value */
+        out << "target.desc=" << btd->target.handle;
+    } else {
+        out << "target.ptr=" << btd->target.ptr;
+    }
+    out << " (cookie " << btd->cookie << ")" << endl
         << "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl
         << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size
         << " bytes)" << endl
         << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size
-        << " bytes)" << endl;
+        << " bytes)";
     return btd+1;
 }
 
 static const void* printReturnCommand(TextOutput& out, const void* _cmd)
 {
-    static const int32_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]);
-    
+    static const size_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]);
     const int32_t* cmd = (const int32_t*)_cmd;
     int32_t code = *cmd++;
-    if (code == BR_ERROR) {
+    size_t cmdIndex = code & 0xff;
+    if (code == (int32_t) BR_ERROR) {
         out << "BR_ERROR: " << (void*)(*cmd++) << endl;
         return cmd;
-    } else if (code < 0 || code >= N) {
+    } else if (cmdIndex >= N) {
         out << "Unknown reply: " << code << endl;
         return cmd;
     }
+    out << kReturnStrings[cmdIndex];
     
-    out << kReturnStrings[code];
     switch (code) {
         case BR_TRANSACTION:
         case BR_REPLY: {
@@ -213,6 +203,11 @@
             const int32_t c = *cmd++;
             out << ": death cookie " << (void*)c;
         } break;
+
+        default:
+            // no details to show for: BR_OK, BR_DEAD_REPLY,
+            // BR_TRANSACTION_COMPLETE, BR_FINISHED
+            break;
     }
     
     out << endl;
@@ -221,16 +216,17 @@
 
 static const void* printCommand(TextOutput& out, const void* _cmd)
 {
-    static const int32_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]);
-    
+    static const size_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]);
     const int32_t* cmd = (const int32_t*)_cmd;
     int32_t code = *cmd++;
-    if (code < 0 || code >= N) {
+    size_t cmdIndex = code & 0xff;
+
+    if (cmdIndex >= N) {
         out << "Unknown command: " << code << endl;
         return cmd;
     }
-    
-    out << kCommandStrings[code];
+    out << kCommandStrings[cmdIndex];
+
     switch (code) {
         case BC_TRANSACTION:
         case BC_REPLY: {
@@ -254,7 +250,7 @@
         case BC_RELEASE:
         case BC_DECREFS: {
             const int32_t d = *cmd++;
-            out << ": descriptor=" << (void*)d;
+            out << ": desc=" << d;
         } break;
     
         case BC_INCREFS_DONE:
@@ -267,7 +263,7 @@
         case BC_ATTEMPT_ACQUIRE: {
             const int32_t p = *cmd++;
             const int32_t d = *cmd++;
-            out << ": decriptor=" << (void*)d << ", pri=" << p;
+            out << ": desc=" << d << ", pri=" << p;
         } break;
         
         case BC_REQUEST_DEATH_NOTIFICATION:
@@ -281,6 +277,11 @@
             const int32_t c = *cmd++;
             out << ": death cookie " << (void*)c;
         } break;
+
+        default:
+            // no details to show for: BC_REGISTER_LOOPER, BC_ENTER_LOOPER,
+            // BC_EXIT_LOOPER
+            break;
     }
     
     out << endl;
@@ -592,6 +593,7 @@
 
 status_t IPCThreadState::attemptIncStrongHandle(int32_t handle)
 {
+    LOG_REMOTEREFS("IPCThreadState::attemptIncStrongHandle(%d)\n", handle);
     mOut.writeInt32(BC_ATTEMPT_ACQUIRE);
     mOut.writeInt32(0); // xxx was thread priority
     mOut.writeInt32(handle);
@@ -772,7 +774,7 @@
     } else {
         bwr.read_size = 0;
     }
-    
+
     IF_LOG_COMMANDS() {
         TextOutput::Bundle _b(alog);
         if (outAvail != 0) {
@@ -789,7 +791,7 @@
     
     // Return immediately if there is nothing to do.
     if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
-    
+
     bwr.write_consumed = 0;
     bwr.read_consumed = 0;
     status_t err;
@@ -809,7 +811,7 @@
             alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
         }
     } while (err == -EINTR);
-    
+
     IF_LOG_COMMANDS() {
         alog << "Our err: " << (void*)err << ", write consumed: "
             << bwr.write_consumed << " (of " << mOut.dataSize()
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 79a01a3..f2bc81e 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -595,7 +595,7 @@
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
-        LOGE("connect: SurfaceTexture has been abandoned!");
+        LOGE("disconnect: SurfaceTexture has been abandoned!");
         return NO_INIT;
     }
 
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 688b998..3de75ba 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -13,6 +13,9 @@
 // Log debug messages about velocity tracking.
 #define DEBUG_VELOCITY 0
 
+// Log debug messages about least squares fitting.
+#define DEBUG_LEAST_SQUARES 0
+
 // Log debug messages about acceleration.
 #define DEBUG_ACCELERATION 0
 
@@ -682,9 +685,61 @@
 
 // --- VelocityTracker ---
 
+const uint32_t VelocityTracker::DEFAULT_DEGREE;
+const nsecs_t VelocityTracker::DEFAULT_HORIZON;
 const uint32_t VelocityTracker::HISTORY_SIZE;
-const nsecs_t VelocityTracker::MAX_AGE;
-const nsecs_t VelocityTracker::MIN_DURATION;
+
+static inline float vectorDot(const float* a, const float* b, uint32_t m) {
+    float r = 0;
+    while (m--) {
+        r += *(a++) * *(b++);
+    }
+    return r;
+}
+
+static inline float vectorNorm(const float* a, uint32_t m) {
+    float r = 0;
+    while (m--) {
+        float t = *(a++);
+        r += t * t;
+    }
+    return sqrtf(r);
+}
+
+#if DEBUG_LEAST_SQUARES || DEBUG_VELOCITY
+static String8 vectorToString(const float* a, uint32_t m) {
+    String8 str;
+    str.append("[");
+    while (m--) {
+        str.appendFormat(" %f", *(a++));
+        if (m) {
+            str.append(",");
+        }
+    }
+    str.append(" ]");
+    return str;
+}
+
+static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
+    String8 str;
+    str.append("[");
+    for (size_t i = 0; i < m; i++) {
+        if (i) {
+            str.append(",");
+        }
+        str.append(" [");
+        for (size_t j = 0; j < n; j++) {
+            if (j) {
+                str.append(",");
+            }
+            str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]);
+        }
+        str.append(" ]");
+    }
+    str.append(" ]");
+    return str;
+}
+#endif
 
 VelocityTracker::VelocityTracker() {
     clear();
@@ -733,16 +788,15 @@
         uint32_t id = iterBits.firstMarkedBit();
         uint32_t index = idBits.getIndexOfBit(id);
         iterBits.clearBit(id);
-        float vx, vy;
-        bool available = getVelocity(id, &vx, &vy);
-        if (available) {
-            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 {
-            LOG_ASSERT(vx == 0 && vy == 0);
-            LOGD("  %d: position (%0.3f, %0.3f), velocity not available",
-                    id, positions[index].x, positions[index].y);
-        }
+        Estimator estimator;
+        getEstimator(id, DEFAULT_DEGREE, DEFAULT_HORIZON, &estimator);
+        LOGD("  %d: position (%0.3f, %0.3f), "
+                "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
+                id, positions[index].x, positions[index].y,
+                int(estimator.degree),
+                vectorToString(estimator.xCoeff, estimator.degree).string(),
+                vectorToString(estimator.yCoeff, estimator.degree).string(),
+                estimator.confidence);
     }
 #endif
 }
@@ -752,6 +806,7 @@
 
     switch (actionMasked) {
     case AMOTION_EVENT_ACTION_DOWN:
+    case AMOTION_EVENT_ACTION_HOVER_ENTER:
         // Clear all pointers on down before adding the new movement.
         clear();
         break;
@@ -764,12 +819,11 @@
         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
+    case AMOTION_EVENT_ACTION_MOVE:
+    case AMOTION_EVENT_ACTION_HOVER_MOVE:
+        break;
+    default:
+        // Ignore all other 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
@@ -811,79 +865,230 @@
     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)) {
-        // Find the oldest sample that contains the pointer and that is not older than MAX_AGE.
-        nsecs_t minTime = newestMovement.eventTime - MAX_AGE;
-        uint32_t oldestIndex = mIndex;
-        uint32_t numTouches = 1;
-        do {
-            uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1;
-            const Movement& nextOldestMovement = mMovements[nextOldestIndex];
-            if (!nextOldestMovement.idBits.hasBit(id)
-                    || nextOldestMovement.eventTime < minTime) {
-                break;
-            }
-            oldestIndex = nextOldestIndex;
-        } while (++numTouches < HISTORY_SIZE);
+/**
+ * Solves a linear least squares problem to obtain a N degree polynomial that fits
+ * the specified input data as nearly as possible.
+ *
+ * Returns true if a solution is found, false otherwise.
+ *
+ * The input consists of two vectors of data points X and Y with indices 0..m-1.
+ * The output is a vector B with indices 0..n-1 that describes a polynomial
+ * that fits the data, such the sum of abs(Y[i] - (B[0] + B[1] X[i] + B[2] X[i]^2 ... B[n] X[i]^n))
+ * for all i between 0 and m-1 is minimized.
+ *
+ * That is to say, the function that generated the input data can be approximated
+ * by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
+ *
+ * The coefficient of determination (R^2) is also returned to describe the goodness
+ * of fit of the model for the given data.  It is a value between 0 and 1, where 1
+ * indicates perfect correspondence.
+ *
+ * This function first expands the X vector to a m by n matrix A such that
+ * A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n.
+ *
+ * Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q
+ * and an m by n upper triangular matrix R.  Because R is upper triangular (lower
+ * part is all zeroes), we can simplify the decomposition into an m by n matrix
+ * Q1 and a n by n matrix R1 such that A = Q1 R1.
+ *
+ * Finally we solve the system of linear equations given by R1 B = (Qtranspose Y)
+ * to find B.
+ *
+ * For efficiency, we lay out A and Q column-wise in memory because we frequently
+ * operate on the column vectors.  Conversely, we lay out R row-wise.
+ *
+ * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares
+ * http://en.wikipedia.org/wiki/Gram-Schmidt
+ */
+static bool solveLeastSquares(const float* x, const float* y, uint32_t m, uint32_t n,
+        float* outB, float* outDet) {
+#if DEBUG_LEAST_SQUARES
+    LOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s", int(m), int(n),
+            vectorToString(x, m).string(), vectorToString(y, m).string());
+#endif
 
-        // 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.
-        //
-        // 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 because
-        // the hardware or driver reports them irregularly or in bursts.
-        float accumVx = 0;
-        float accumVy = 0;
-        uint32_t index = oldestIndex;
-        uint32_t samplesUsed = 0;
-        const Movement& oldestMovement = mMovements[oldestIndex];
-        const Position& oldestPosition =
-                oldestMovement.positions[oldestMovement.idBits.getIndexOfBit(id)];
-        nsecs_t lastDuration = 0;
+    // Expand the X vector to a matrix A.
+    float a[n][m]; // column-major order
+    for (uint32_t h = 0; h < m; h++) {
+        a[0][h] = 1;
+        for (uint32_t i = 1; i < n; i++) {
+            a[i][h] = a[i - 1][h] * x[h];
+        }
+    }
+#if DEBUG_LEAST_SQUARES
+    LOGD("  - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string());
+#endif
 
-        while (numTouches-- > 1) {
-            if (++index == HISTORY_SIZE) {
-                index = 0;
-            }
-            const Movement& movement = mMovements[index];
-            nsecs_t duration = movement.eventTime - oldestMovement.eventTime;
-
-            // 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 * lastDuration + vx * duration) / (duration + lastDuration);
-                accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
-
-                lastDuration = duration;
-                samplesUsed += 1;
+    // Apply the Gram-Schmidt process to A to obtain its QR decomposition.
+    float q[n][m]; // orthonormal basis, column-major order
+    float r[n][n]; // upper triangular matrix, row-major order
+    for (uint32_t j = 0; j < n; j++) {
+        for (uint32_t h = 0; h < m; h++) {
+            q[j][h] = a[j][h];
+        }
+        for (uint32_t i = 0; i < j; i++) {
+            float dot = vectorDot(&q[j][0], &q[i][0], m);
+            for (uint32_t h = 0; h < m; h++) {
+                q[j][h] -= dot * q[i][h];
             }
         }
 
-        // Make sure we used at least one sample.
-        if (samplesUsed != 0) {
-            *outVx = accumVx;
-            *outVy = accumVy;
+        float norm = vectorNorm(&q[j][0], m);
+        if (norm < 0.000001f) {
+            // vectors are linearly dependent or zero so no solution
+#if DEBUG_LEAST_SQUARES
+            LOGD("  - no solution, norm=%f", norm);
+#endif
+            return false;
+        }
+
+        float invNorm = 1.0f / norm;
+        for (uint32_t h = 0; h < m; h++) {
+            q[j][h] *= invNorm;
+        }
+        for (uint32_t i = 0; i < n; i++) {
+            r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
+        }
+    }
+#if DEBUG_LEAST_SQUARES
+    LOGD("  - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string());
+    LOGD("  - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string());
+
+    // calculate QR, if we factored A correctly then QR should equal A
+    float qr[n][m];
+    for (uint32_t h = 0; h < m; h++) {
+        for (uint32_t i = 0; i < n; i++) {
+            qr[i][h] = 0;
+            for (uint32_t j = 0; j < n; j++) {
+                qr[i][h] += q[j][h] * r[j][i];
+            }
+        }
+    }
+    LOGD("  - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string());
+#endif
+
+    // Solve R B = Qt Y to find B.  This is easy because R is upper triangular.
+    // We just work from bottom-right to top-left calculating B's coefficients.
+    for (uint32_t i = n; i-- != 0; ) {
+        outB[i] = vectorDot(&q[i][0], y, m);
+        for (uint32_t j = n - 1; j > i; j--) {
+            outB[i] -= r[i][j] * outB[j];
+        }
+        outB[i] /= r[i][i];
+    }
+#if DEBUG_LEAST_SQUARES
+    LOGD("  - b=%s", vectorToString(outB, n).string());
+#endif
+
+    // Calculate the coefficient of determination as 1 - (SSerr / SStot) where
+    // SSerr is the residual sum of squares (squared variance of the error),
+    // and SStot is the total sum of squares (squared variance of the data).
+    float ymean = 0;
+    for (uint32_t h = 0; h < m; h++) {
+        ymean += y[h];
+    }
+    ymean /= m;
+
+    float sserr = 0;
+    float sstot = 0;
+    for (uint32_t h = 0; h < m; h++) {
+        float err = y[h] - outB[0];
+        float term = 1;
+        for (uint32_t i = 1; i < n; i++) {
+            term *= x[h];
+            err -= term * outB[i];
+        }
+        sserr += err * err;
+        float var = y[h] - ymean;
+        sstot += var * var;
+    }
+    *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
+#if DEBUG_LEAST_SQUARES
+    LOGD("  - sserr=%f", sserr);
+    LOGD("  - sstot=%f", sstot);
+    LOGD("  - det=%f", *outDet);
+#endif
+    return true;
+}
+
+bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
+    Estimator estimator;
+    if (getEstimator(id, DEFAULT_DEGREE, DEFAULT_HORIZON, &estimator)) {
+        if (estimator.degree >= 1) {
+            *outVx = estimator.xCoeff[1];
+            *outVy = estimator.yCoeff[1];
+            return true;
+        }
+    }
+    *outVx = 0;
+    *outVy = 0;
+    return false;
+}
+
+bool VelocityTracker::getEstimator(uint32_t id, uint32_t degree, nsecs_t horizon,
+        Estimator* outEstimator) const {
+    outEstimator->clear();
+
+    // Iterate over movement samples in reverse time order and collect samples.
+    float x[HISTORY_SIZE];
+    float y[HISTORY_SIZE];
+    float time[HISTORY_SIZE];
+    uint32_t m = 0;
+    uint32_t index = mIndex;
+    const Movement& newestMovement = mMovements[mIndex];
+    do {
+        const Movement& movement = mMovements[index];
+        if (!movement.idBits.hasBit(id)) {
+            break;
+        }
+
+        nsecs_t age = newestMovement.eventTime - movement.eventTime;
+        if (age > horizon) {
+            break;
+        }
+
+        const Position& position = movement.getPosition(id);
+        x[m] = position.x;
+        y[m] = position.y;
+        time[m] = -age * 0.000000001f;
+        index = (index == 0 ? HISTORY_SIZE : index) - 1;
+    } while (++m < HISTORY_SIZE);
+
+    if (m == 0) {
+        return false; // no data
+    }
+
+    // Calculate a least squares polynomial fit.
+    if (degree > Estimator::MAX_DEGREE) {
+        degree = Estimator::MAX_DEGREE;
+    }
+    if (degree > m - 1) {
+        degree = m - 1;
+    }
+    if (degree >= 1) {
+        float xdet, ydet;
+        uint32_t n = degree + 1;
+        if (solveLeastSquares(time, x, m, n, outEstimator->xCoeff, &xdet)
+                && solveLeastSquares(time, y, m, n, outEstimator->yCoeff, &ydet)) {
+            outEstimator->degree = degree;
+            outEstimator->confidence = xdet * ydet;
+#if DEBUG_LEAST_SQUARES
+            LOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
+                    int(outEstimator->degree),
+                    vectorToString(outEstimator->xCoeff, n).string(),
+                    vectorToString(outEstimator->yCoeff, n).string(),
+                    outEstimator->confidence);
+#endif
             return true;
         }
     }
 
-    // No data available for this pointer.
-    *outVx = 0;
-    *outVy = 0;
-    return false;
+    // No velocity data available for this pointer, but we do have its current position.
+    outEstimator->xCoeff[0] = x[0];
+    outEstimator->yCoeff[0] = y[0];
+    outEstimator->degree = 0;
+    outEstimator->confidence = 1;
+    return true;
 }
 
 
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index a060a5f..8dab291 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -479,6 +479,11 @@
         const Region& lhs,
         const Rect& rhs, int dx, int dy)
 {
+    if (!rhs.isValid()) {
+        LOGE("Region::boolean_operation(op=%d) invalid Rect={%d,%d,%d,%d}",
+                op, rhs.left, rhs.top, rhs.right, rhs.bottom);
+    }
+
 #if VALIDATE_WITH_CORECG || VALIDATE_REGIONS
     boolean_operation(op, dst, lhs, Region(rhs), dx, dy);
 #else
diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp
index 4dfa578..ceca435 100644
--- a/libs/utils/Static.cpp
+++ b/libs/utils/Static.cpp
@@ -56,7 +56,9 @@
 protected:
     virtual status_t writeLines(const struct iovec& vec, size_t N)
     {
-        android_writevLog(&vec, N);
+        //android_writevLog(&vec, N);       <-- this is now a no-op
+        if (N != 1) LOGI("WARNING: writeLines N=%d\n", N);
+        LOGI("%.*s", vec.iov_len, (const char*) vec.iov_base);
         return NO_ERROR;
     }
 };
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 02c380b..5dbcb75 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -368,6 +368,14 @@
     return rc;
 }
 
+int androidGetThreadPriority(pid_t tid) {
+#if defined(HAVE_PTHREADS)
+    return getpriority(PRIO_PROCESS, tid);
+#else
+    return ANDROID_PRIORITY_NORMAL;
+#endif
+}
+
 int androidGetThreadSchedulingGroup(pid_t tid)
 {
     int ret = ANDROID_TGROUP_DEFAULT;
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 6a199db..a08932a 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -242,6 +242,20 @@
 #define EGL_RECORDABLE_ANDROID                  0x3142  /* EGLConfig attribute */
 #endif
 
+/* EGL_NV_system_time
+ */
+#ifndef EGL_NV_system_time
+#define EGL_NV_system_time 1
+typedef khronos_int64_t EGLint64NV;
+typedef khronos_uint64_t EGLuint64NV;
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLuint64NV EGLAPIENTRY eglGetSystemTimeFrequencyNV(void);
+EGLAPI EGLuint64NV EGLAPIENTRY eglGetSystemTimeNV(void);
+#endif
+typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMEFREQUENCYNVPROC)(void);
+typedef EGLuint64NV (EGLAPIENTRYP PFNEGLGETSYSTEMTIMENVPROC)(void);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 7e85230..23f67d5 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -62,6 +62,7 @@
         "EGL_KHR_fence_sync "
         "EGL_ANDROID_image_native_buffer "
         "EGL_ANDROID_swap_rectangle "
+        "EGL_NV_system_time "
         ;
 
 struct extention_map_t {
@@ -80,6 +81,10 @@
             (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
     { "eglSetSwapRectangleANDROID",
             (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID },
+    { "eglGetSystemTimeFrequencyNV",
+            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
+    { "eglGetSystemTimeNV",
+            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
 };
 
 // accesses protected by sExtensionMapMutex
@@ -1454,3 +1459,46 @@
     }
     return setError(EGL_BAD_DISPLAY, NULL);
 }
+
+// ----------------------------------------------------------------------------
+// NVIDIA extensions
+// ----------------------------------------------------------------------------
+EGLuint64NV eglGetSystemTimeFrequencyNV()
+{
+    clearError();
+
+    if (egl_init_drivers() == EGL_FALSE) {
+        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    }
+
+    EGLuint64NV ret = 0;
+    egl_connection_t* const cnx = &gEGLImpl[IMPL_HARDWARE];
+
+    if (cnx->dso) {
+        if (cnx->egl.eglGetSystemTimeFrequencyNV) {
+            return cnx->egl.eglGetSystemTimeFrequencyNV();
+        }
+    }
+
+    return setError(EGL_BAD_DISPLAY, 0);;
+}
+
+EGLuint64NV eglGetSystemTimeNV()
+{
+    clearError();
+
+    if (egl_init_drivers() == EGL_FALSE) {
+        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    }
+
+    EGLuint64NV ret = 0;
+    egl_connection_t* const cnx = &gEGLImpl[IMPL_HARDWARE];
+
+    if (cnx->dso) {
+        if (cnx->egl.eglGetSystemTimeNV) {
+            return cnx->egl.eglGetSystemTimeNV();
+        }
+    }
+
+    return setError(EGL_BAD_DISPLAY, 0);;
+}
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 63c3c19..bdd2a7e 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -62,3 +62,8 @@
 
 EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint)
 EGL_ENTRY(EGLClientBuffer, eglGetRenderBufferANDROID, EGLDisplay, EGLSurface)
+
+/* NVIDIA extensions */
+
+EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void)
+EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void)
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index d2b7378..46f7139 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -130,7 +130,7 @@
         if (window != NULL) {
             native_window_set_buffers_format(window, 0);
             if (native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL)) {
-                LOGE("EGLNativeWindowType %p disconnected failed", window);
+                LOGW("EGLNativeWindowType %p disconnect failed", window);
             }
         }
     }
diff --git a/opengl/libs/GLES2_dbg/Android.mk b/opengl/libs/GLES2_dbg/Android.mk
index c2b1142..70853d8 100644
--- a/opengl/libs/GLES2_dbg/Android.mk
+++ b/opengl/libs/GLES2_dbg/Android.mk
@@ -31,6 +31,9 @@
     LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
 endif
 
+LOCAL_CFLAGS += -DLOG_TAG=\"libGLES2_dbg\"
+
+
 # we need to access the private Bionic header <bionic_tls.h>
 # on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER
 # behavior from the bionic Android.mk file
diff --git a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
index 9e77665..41061e1 100644
--- a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
+++ b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
@@ -30,13 +30,11 @@
 }
 
 DbgContext::DbgContext(const unsigned version, const gl_hooks_t * const hooks,
-                       const unsigned MAX_VERTEX_ATTRIBS, const GLenum readFormat,
-                       const GLenum readType)
+                       const unsigned MAX_VERTEX_ATTRIBS)
         : lzf_buf(NULL), lzf_readIndex(0), lzf_refSize(0), lzf_refBufSize(0)
         , version(version), hooks(hooks)
         , MAX_VERTEX_ATTRIBS(MAX_VERTEX_ATTRIBS)
-        , readFormat(readFormat), readType(readType)
-        , readBytesPerPixel(GetBytesPerPixel(readFormat, readType))
+        , readBytesPerPixel(4)
         , captureSwap(0), captureDraw(0)
         , vertexAttribs(new VertexAttrib[MAX_VERTEX_ATTRIBS])
         , hasNonVBOAttribs(false), indexBuffers(NULL), indexBuffer(NULL)
@@ -67,11 +65,7 @@
     assert(GL_NO_ERROR == hooks->gl.glGetError());
     GLint MAX_VERTEX_ATTRIBS = 0;
     hooks->gl.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &MAX_VERTEX_ATTRIBS);
-    GLint readFormat, readType;
-    hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat);
-    hooks->gl.glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType);
-    DbgContext* dbg = new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS, readFormat, readType);
-
+    DbgContext* dbg = new DbgContext(version, hooks, MAX_VERTEX_ATTRIBS);
     glesv2debugger::Message msg, cmd;
     msg.set_context_id(reinterpret_cast<int>(dbg));
     msg.set_expect_response(false);
@@ -100,33 +94,31 @@
 {
     switch (type) {
     case GL_UNSIGNED_SHORT_5_6_5:
-        return 2;
     case GL_UNSIGNED_SHORT_4_4_4_4:
-        return 2;
     case GL_UNSIGNED_SHORT_5_5_5_1:
         return 2;
     case GL_UNSIGNED_BYTE:
         break;
     default:
-        assert(0);
+        LOGE("GetBytesPerPixel: unknown type %x", type);
     }
 
     switch (format) {
     case GL_ALPHA:
-        return 1;
     case GL_LUMINANCE:
         return 1;
-        break;
     case GL_LUMINANCE_ALPHA:
         return 2;
     case GL_RGB:
         return 3;
     case GL_RGBA:
+    case 0x80E1:   // GL_BGRA_EXT
         return 4;
     default:
-        assert(0);
-        return 0;
+        LOGE("GetBytesPerPixel: unknown format %x", format);
     }
+
+    return 1; // in doubt...
 }
 
 void DbgContext::Fetch(const unsigned index, std::string * const data) const
diff --git a/opengl/libs/GLES2_dbg/src/egl.cpp b/opengl/libs/GLES2_dbg/src/egl.cpp
index eb28d06..bbea3bd 100644
--- a/opengl/libs/GLES2_dbg/src/egl.cpp
+++ b/opengl/libs/GLES2_dbg/src/egl.cpp
@@ -41,11 +41,11 @@
         void * pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
                         dbg->readBytesPerPixel);
         dbg->hooks->gl.glReadPixels(viewport[0], viewport[1], viewport[2],
-                                    viewport[3], dbg->readFormat, dbg->readType, pixels);
+                                    viewport[3], GL_RGBA, GL_UNSIGNED_BYTE, pixels);
         dbg->CompressReadPixelBuffer(msg.mutable_data());
         msg.set_data_type(msg.ReferencedImage);
-        msg.set_pixel_format(dbg->readFormat);
-        msg.set_pixel_type(dbg->readType);
+        msg.set_pixel_format(GL_RGBA);
+        msg.set_pixel_type(GL_UNSIGNED_BYTE);
         msg.set_image_width(viewport[2]);
         msg.set_image_height(viewport[3]);
     }
diff --git a/opengl/libs/GLES2_dbg/src/header.h b/opengl/libs/GLES2_dbg/src/header.h
index f2b1fa6..49f3847 100644
--- a/opengl/libs/GLES2_dbg/src/header.h
+++ b/opengl/libs/GLES2_dbg/src/header.h
@@ -87,7 +87,6 @@
     const unsigned int version; // 0 is GLES1, 1 is GLES2
     const gl_hooks_t * const hooks;
     const unsigned int MAX_VERTEX_ATTRIBS;
-    const GLenum readFormat, readType; // implementation supported glReadPixels
     const unsigned int readBytesPerPixel;
 
     unsigned int captureSwap; // number of eglSwapBuffers to glReadPixels
@@ -124,8 +123,7 @@
     unsigned maxAttrib; // number of slots used by program
 
     DbgContext(const unsigned version, const gl_hooks_t * const hooks,
-               const unsigned MAX_VERTEX_ATTRIBS, const GLenum readFormat,
-               const GLenum readType);
+               const unsigned MAX_VERTEX_ATTRIBS);
     ~DbgContext();
 
     void Fetch(const unsigned index, std::string * const data) const;
diff --git a/opengl/libs/GLES2_dbg/src/vertex.cpp b/opengl/libs/GLES2_dbg/src/vertex.cpp
index 029ee3b..28a2420 100644
--- a/opengl/libs/GLES2_dbg/src/vertex.cpp
+++ b/opengl/libs/GLES2_dbg/src/vertex.cpp
@@ -77,7 +77,7 @@
                 pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
                                                   dbg->readBytesPerPixel);
                 Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
-                                   dbg->readFormat, dbg->readType, pixels);
+                        GL_RGBA, GL_UNSIGNED_BYTE, pixels);
             }
             break;
         case glesv2debugger::Message_Function_SKIP:
@@ -134,19 +134,22 @@
     msg.set_arg7(dbg->maxAttrib); // indicate capturing vertex data
     std::string * const data = msg.mutable_data();
     if (GL_UNSIGNED_BYTE == type) {
-        if (dbg->indexBuffer)
+        if (dbg->indexBuffer) {
             FetchIndexed(count, (unsigned char *)dbg->indexBuffer->data +
                          (unsigned long)indices, data, dbg);
-        else
+        } else {
             FetchIndexed(count, (unsigned char *)indices, data, dbg);
+        }
     } else if (GL_UNSIGNED_SHORT == type) {
-        if (dbg->indexBuffer)
+        if (dbg->indexBuffer) {
             FetchIndexed(count, (unsigned short *)((char *)dbg->indexBuffer->data +
                                                    (unsigned long)indices), data, dbg);
-        else
+        } else {
             FetchIndexed(count, (unsigned short *)indices, data, dbg);
-    } else
+        }
+    } else {
         assert(0);
+    }
 
     void * pixels = NULL;
     int viewport[4] = {};
@@ -174,7 +177,7 @@
             Send(msg, cmd);
             expectResponse = cmd.expect_response();
             // TODO: pack glReadPixels data with vertex data instead of
-            //  relying on sperate call for transport, this would allow
+            //  relying on separate call for transport, this would allow
             //  auto generated message loop using EXTEND_Debug macro
             if (dbg->captureDraw > 0) {
                 dbg->captureDraw--;
@@ -182,7 +185,7 @@
                 pixels = dbg->GetReadPixelsBuffer(viewport[2] * viewport[3] *
                                                   dbg->readBytesPerPixel);
                 Debug_glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
-                                   dbg->readFormat, dbg->readType, pixels);
+                        GL_RGBA, GL_UNSIGNED_BYTE, pixels);
             }
             break;
         case glesv2debugger::Message_Function_SKIP:
diff --git a/opengl/libs/GLES2_dbg/test/test_main.cpp b/opengl/libs/GLES2_dbg/test/test_main.cpp
index 058bea4..183bf8e 100644
--- a/opengl/libs/GLES2_dbg/test/test_main.cpp
+++ b/opengl/libs/GLES2_dbg/test/test_main.cpp
@@ -29,7 +29,7 @@
     gl_hooks_t hooks;
 
     DbgContextTest()
-            : dbg(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE) {
+            : dbg(1, &hooks, 32) {
         // You can do set-up work for each test here.
         hooks.gl.glGetError = GetError;
     }
diff --git a/opengl/libs/GLES2_dbg/test/test_server.cpp b/opengl/libs/GLES2_dbg/test/test_server.cpp
index bbbe913..0ab87b0 100644
--- a/opengl/libs/GLES2_dbg/test/test_server.cpp
+++ b/opengl/libs/GLES2_dbg/test/test_server.cpp
@@ -151,7 +151,7 @@
     virtual void SetUp() {
         ServerFileTest::SetUp();
 
-        dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE);
+        dbg = new DbgContext(1, &hooks, 32);
         ASSERT_NE((void *)NULL, dbg);
         for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++)
             ((void **)&hooks)[i] = reinterpret_cast<void *>(glNoop);
diff --git a/opengl/libs/GLES2_dbg/test/test_socket.cpp b/opengl/libs/GLES2_dbg/test/test_socket.cpp
index b2148ac..9f815e2 100644
--- a/opengl/libs/GLES2_dbg/test/test_socket.cpp
+++ b/opengl/libs/GLES2_dbg/test/test_socket.cpp
@@ -44,7 +44,7 @@
     }
 
     virtual void SetUp() {
-        dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE);
+        dbg = new DbgContext(1, &hooks, 32);
         ASSERT_TRUE(dbg != NULL);
         for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++)
             ((void **)&hooks)[i] = (void *)glNoop;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index 40a6f1e..f02c954 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -84,9 +84,10 @@
     
     status_t compositionComplete() const;
     
-    Rect bounds() const {
+    Rect getBounds() const {
         return Rect(mWidth, mHeight);
     }
+    inline Rect bounds() const { return getBounds(); }
 
     // only for debugging
     int getCurrentBufferIndex() const;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 0ff1cce..e707bdc 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -22,6 +22,7 @@
 
 #include <utils/Errors.h>
 #include <utils/String8.h>
+#include <utils/Vector.h>
 
 #include <hardware/hardware.h>
 
@@ -29,6 +30,7 @@
 
 #include <EGL/egl.h>
 
+#include "LayerBase.h"
 #include "HWComposer.h"
 #include "SurfaceFlinger.h"
 
@@ -38,6 +40,7 @@
 HWComposer::HWComposer(const sp<SurfaceFlinger>& flinger)
     : mFlinger(flinger),
       mModule(0), mHwc(0), mList(0), mCapacity(0),
+      mNumOVLayers(0), mNumFBLayers(0),
       mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE)
 {
     int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
@@ -96,9 +99,40 @@
 
 status_t HWComposer::prepare() const {
     int err = mHwc->prepare(mHwc, mList);
+    if (err == NO_ERROR) {
+        size_t numOVLayers = 0;
+        size_t numFBLayers = 0;
+        size_t count = mList->numHwLayers;
+        for (size_t i=0 ; i<count ; i++) {
+            hwc_layer& l(mList->hwLayers[i]);
+            if (l.flags & HWC_SKIP_LAYER) {
+                l.compositionType = HWC_FRAMEBUFFER;
+            }
+            switch (l.compositionType) {
+                case HWC_OVERLAY:
+                    numOVLayers++;
+                    break;
+                case HWC_FRAMEBUFFER:
+                    numFBLayers++;
+                    break;
+            }
+        }
+        mNumOVLayers = numOVLayers;
+        mNumFBLayers = numFBLayers;
+    }
     return (status_t)err;
 }
 
+size_t HWComposer::getLayerCount(int type) const {
+    switch (type) {
+        case HWC_OVERLAY:
+            return mNumOVLayers;
+        case HWC_FRAMEBUFFER:
+            return mNumFBLayers;
+    }
+    return 0;
+}
+
 status_t HWComposer::commit() const {
     int err = mHwc->set(mHwc, mDpy, mSur, mList);
     if (mList) {
@@ -133,24 +167,37 @@
     return mList ? mList->hwLayers : 0;
 }
 
-void HWComposer::dump(String8& result, char* buffer, size_t SIZE) const {
+void HWComposer::dump(String8& result, char* buffer, size_t SIZE,
+        const Vector< sp<LayerBase> >& visibleLayersSortedByZ) 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);
-
+        result.append(
+                "    type   |   hints  |   flags  | tr | blend |  format  |     source rectangle      |      crop rectangle       name \n"
+                "-----------+----------+----------+----+-------+----------+---------------------------+--------------------------------\n");
+        //      "  ________ | ________ | ________ | __ | _____ | ________ | [_____,_____,_____,_____] | [_____,_____,_____,_____]
         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",
+            const sp<LayerBase> layer(visibleLayersSortedByZ[i]);
+            int32_t format = -1;
+            if (layer->getLayer() != NULL) {
+                const sp<GraphicBuffer>& buffer(layer->getLayer()->getActiveBuffer());
+                if (buffer != NULL) {
+                    format = buffer->getPixelFormat();
+                }
+            }
+            snprintf(buffer, SIZE,
+                    "  %8s | %08x | %08x | %02x | %05x | %08x | [%5d,%5d,%5d,%5d] | [%5d,%5d,%5d,%5d] %s\n",
                     l.compositionType ? "OVERLAY" : "FB",
-                    l.hints, l.flags, l.transform, l.blending,
+                    l.hints, l.flags, l.transform, l.blending, format,
                     l.sourceCrop.left, l.sourceCrop.top, l.sourceCrop.right, l.sourceCrop.bottom,
-                    l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom);
+                    l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom,
+                    layer->getName().string());
             result.append(buffer);
         }
-
     }
     if (mHwc && mHwc->common.version >= 1 && mHwc->dump) {
         mHwc->dump(mHwc, buffer, SIZE);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 77c1a4b..aa8ebe1 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -25,12 +25,14 @@
 #include <hardware/hwcomposer.h>
 
 #include <utils/StrongPointer.h>
+#include <utils/Vector.h>
 
 namespace android {
 // ---------------------------------------------------------------------------
 
 class String8;
 class SurfaceFlinger;
+class LayerBase;
 
 class HWComposer
 {
@@ -62,8 +64,12 @@
     size_t getNumLayers() const;
     hwc_layer_t* getLayers() const;
 
+    // updated in preapre()
+    size_t getLayerCount(int type) const;
+
     // for debugging
-    void dump(String8& out, char* scratch, size_t SIZE) const;
+    void dump(String8& out, char* scratch, size_t SIZE,
+            const Vector< sp<LayerBase> >& visibleLayersSortedByZ) const;
 
 private:
     struct cb_context {
@@ -78,6 +84,8 @@
     hwc_composer_device_t*  mHwc;
     hwc_layer_list_t*       mList;
     size_t                  mCapacity;
+    mutable size_t          mNumOVLayers;
+    mutable size_t          mNumFBLayers;
     hwc_display_t           mDpy;
     hwc_surface_t           mSur;
     cb_context              mCBContext;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index edbc7b0..f85ce7f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -404,7 +404,9 @@
 void Layer::lockPageFlip(bool& recomputeVisibleRegions)
 {
     if (mQueuedFrames > 0) {
+        // Capture the old state of the layer for comparisons later
         const bool oldOpacity = isOpaque();
+        sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer;
 
         // signal another event if we have more frames pending
         if (android_atomic_dec(&mQueuedFrames) > 1) {
@@ -417,7 +419,8 @@
             return;
         }
 
-        sp<GraphicBuffer> newFrontBuffer(mSurfaceTexture->getCurrentBuffer());
+        // update the active buffer
+        mActiveBuffer = mSurfaceTexture->getCurrentBuffer();
 
         const Rect crop(mSurfaceTexture->getCurrentCrop());
         const uint32_t transform(mSurfaceTexture->getCurrentTransform());
@@ -439,16 +442,16 @@
             mFlinger->invalidateHwcGeometry();
         }
 
-        uint32_t bufWidth  = newFrontBuffer->getWidth();
-        uint32_t bufHeight = newFrontBuffer->getHeight();
-        if (mActiveBuffer != NULL) {
-            if (bufWidth != uint32_t(mActiveBuffer->width) ||
-                bufHeight != uint32_t(mActiveBuffer->height)) {
+        uint32_t bufWidth  = mActiveBuffer->getWidth();
+        uint32_t bufHeight = mActiveBuffer->getHeight();
+        if (oldActiveBuffer != NULL) {
+            if (bufWidth != uint32_t(oldActiveBuffer->width) ||
+                bufHeight != uint32_t(oldActiveBuffer->height)) {
                 mFlinger->invalidateHwcGeometry();
             }
         }
 
-        mCurrentOpacity = getOpacityForFormat(newFrontBuffer->format);
+        mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format);
         if (oldOpacity != isOpaque()) {
             recomputeVisibleRegions = true;
         }
@@ -462,9 +465,6 @@
         // FIXME: mPostedDirtyRegion = dirty & bounds
         mPostedDirtyRegion.set(front.w, front.h);
 
-        // update active buffer
-        mActiveBuffer = newFrontBuffer;
-
         if ((front.w != front.requested_w) ||
             (front.h != front.requested_h))
         {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d06a35f..ff389ae 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -73,12 +73,14 @@
     virtual bool isSecure() const           { return mSecure; }
     virtual bool isProtected() const;
     virtual void onRemoved();
+    virtual sp<Layer> getLayer() const { return const_cast<Layer*>(this); }
 
     // LayerBaseClient interface
     virtual wp<IBinder> getSurfaceTextureBinder() const;
 
     // only for debugging
     inline const sp<FreezeLock>&  getFreezeLock() const { return mFreezeLock; }
+    inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; }
 
 protected:
     virtual void onFirstRef();
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 603fb60..e5ce814 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -43,7 +43,7 @@
     : dpy(display), contentDirty(false),
       sequence(uint32_t(android_atomic_inc(&sSequence))),
       mFlinger(flinger), mFiltering(false),
-      mNeedsFiltering(false),
+      mNeedsFiltering(false), mInOverlay(false),
       mOrientation(0),
       mTransactionFlags(0),
       mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
@@ -344,6 +344,14 @@
     hwcl->handle = NULL;
 }
 
+void LayerBase::setOverlay(bool inOverlay) {
+    mInOverlay = inOverlay;
+}
+
+bool LayerBase::isOverlay() const {
+    return mInOverlay;
+}
+
 void LayerBase::setFiltering(bool filtering)
 {
     mFiltering = filtering;
@@ -472,12 +480,13 @@
 {
     const Layer::State& s(drawingState());
     snprintf(buffer, SIZE,
-            "+ %s %p\n"
+            "+ %s %p (%s)\n"
             "      "
             "z=%9d, pos=(%g,%g), size=(%4d,%4d), "
             "isOpaque=%1d, needsDithering=%1d, invalidate=%1d, "
             "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
-            getTypeId(), this, s.z, s.transform.tx(), s.transform.ty(), s.w, s.h,
+            getTypeId(), this, getName().string(),
+            s.z, s.transform.tx(), s.transform.ty(), s.w, s.h,
             isOpaque(), needsDithering(), contentDirty,
             s.alpha, s.flags,
             s.transform[0][0], s.transform[0][1],
@@ -553,9 +562,7 @@
 
     sp<Client> client(mClientRef.promote());
     snprintf(buffer, SIZE,
-            "      name=%s\n"
             "      client=%p, identity=%u\n",
-            getName().string(),
             client.get(), getIdentity());
 
     result.append(buffer);
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index d20f06a..a14b397 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -46,6 +46,7 @@
 class DisplayHardware;
 class GraphicBuffer;
 class GraphicPlane;
+class Layer;
 class LayerBaseClient;
 class SurfaceFlinger;
 
@@ -105,12 +106,15 @@
             void invalidate();
 
     virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; }
+    virtual sp<Layer> getLayer() const { return 0; }
 
     virtual const char* getTypeId() const { return "LayerBase"; }
 
     virtual void setGeometry(hwc_layer_t* hwcl);
-
     virtual void setPerFrameData(hwc_layer_t* hwcl);
+            void setOverlay(bool inOverlay);
+            bool isOverlay() const;
+
 
     /**
      * draw - performs some global clipping optimizations
@@ -242,6 +246,11 @@
                 // Whether filtering is needed b/c of the drawingstate
                 bool            mNeedsFiltering;
 
+                // this layer is currently handled by the hwc. this is
+                // updated at composition time, always frmo the composition
+                // thread.
+                bool            mInOverlay;
+
 protected:
                 // cached during validateVisibility()
                 int32_t         mOrientation;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4a3a8ea..0ef03bb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -469,14 +469,14 @@
 
 void SurfaceFlinger::postFramebuffer()
 {
-    if (!mInvalidRegion.isEmpty()) {
+    if (!mSwapRegion.isEmpty()) {
         const DisplayHardware& hw(graphicPlane(0).displayHardware());
         const nsecs_t now = systemTime();
         mDebugInSwapBuffers = now;
-        hw.flip(mInvalidRegion);
+        hw.flip(mSwapRegion);
         mLastSwapBufferTime = systemTime() - now;
         mDebugInSwapBuffers = 0;
-        mInvalidRegion.clear();
+        mSwapRegion.clear();
     }
 }
 
@@ -817,20 +817,6 @@
     mHwWorkListDirty = false;
     HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
     if (hwc.initCheck() == NO_ERROR) {
-
-        const DisplayHardware& hw(graphicPlane(0).displayHardware());
-        uint32_t flags = hw.getFlags();
-        if ((flags & DisplayHardware::SWAP_RECTANGLE) ||
-            (flags & DisplayHardware::BUFFER_PRESERVED))
-        {
-            // we need to redraw everything (the whole screen)
-            // NOTE: we could be more subtle here and redraw only
-            // the area which will end-up in an overlay. But since this
-            // shouldn't happen often, we invalidate everything.
-            mDirtyRegion.set(hw.bounds());
-            mInvalidRegion = mDirtyRegion;
-        }
-
         const Vector< sp<LayerBase> >& currentLayers(mVisibleLayersSortedByZ);
         const size_t count = currentLayers.size();
         hwc.createWorkList(count);
@@ -848,7 +834,7 @@
 void SurfaceFlinger::handleRepaint()
 {
     // compute the invalid region
-    mInvalidRegion.orSelf(mDirtyRegion);
+    mSwapRegion.orSelf(mDirtyRegion);
 
     if (UNLIKELY(mDebugRegion)) {
         debugFlashRegions();
@@ -869,7 +855,7 @@
         if (flags & DisplayHardware::SWAP_RECTANGLE) {
             // TODO: we really should be able to pass a region to
             // SWAP_RECTANGLE so that we don't have to redraw all this.
-            mDirtyRegion.set(mInvalidRegion.bounds());
+            mDirtyRegion.set(mSwapRegion.bounds());
         } else {
             // in the BUFFER_PRESERVED case, obviously, we can update only
             // what's needed and nothing more.
@@ -882,38 +868,35 @@
             // (pushed to the framebuffer).
             // This is needed because PARTIAL_UPDATES only takes one
             // rectangle instead of a region (see DisplayHardware::flip())
-            mDirtyRegion.set(mInvalidRegion.bounds());
+            mDirtyRegion.set(mSwapRegion.bounds());
         } else {
             // we need to redraw everything (the whole screen)
             mDirtyRegion.set(hw.bounds());
-            mInvalidRegion = mDirtyRegion;
+            mSwapRegion = mDirtyRegion;
         }
     }
 
-    // compose all surfaces
+    setupHardwareComposer(mDirtyRegion);
     composeSurfaces(mDirtyRegion);
 
-    // clear the dirty regions
+    // update the swap region and clear the dirty region
+    mSwapRegion.orSelf(mDirtyRegion);
     mDirtyRegion.clear();
 }
 
-void SurfaceFlinger::composeSurfaces(const Region& dirty)
+void SurfaceFlinger::setupHardwareComposer(Region& dirtyInOut)
 {
-    if (UNLIKELY(!mWormholeRegion.isEmpty())) {
-        // should never happen unless the window manager has a bug
-        // draw something...
-        drawWormhole();
-    }
-
-    status_t err = NO_ERROR;
-    const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
-    size_t count = layers.size();
-
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     HWComposer& hwc(hw.getHwComposer());
     hwc_layer_t* const cur(hwc.getLayers());
+    if (!cur) {
+        return;
+    }
 
-    LOGE_IF(cur && hwc.getNumLayers() != count,
+    const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+    size_t count = layers.size();
+
+    LOGE_IF(hwc.getNumLayers() != count,
             "HAL number of layers (%d) doesn't match surfaceflinger (%d)",
             hwc.getNumLayers(), count);
 
@@ -926,57 +909,102 @@
      *  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]);
-        }
-        err = hwc.prepare();
-        LOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
+    for (size_t i=0 ; i<count ; i++) {
+        const sp<LayerBase>& layer(layers[i]);
+        layer->setPerFrameData(&cur[i]);
+    }
+    const size_t fbLayerCount = hwc.getLayerCount(HWC_FRAMEBUFFER);
+    status_t err = hwc.prepare();
+    LOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
 
-        if (err == NO_ERROR) {
+    if (err == NO_ERROR) {
+        // what's happening here is tricky.
+        // we want to clear all the layers with the CLEAR_FB flags
+        // that are opaque.
+        // however, since some GPU are efficient at preserving
+        // the backbuffer, we want to take advantage of that so we do the
+        // clear only in the dirty region (other areas will be preserved
+        // on those GPUs).
+        //   NOTE: on non backbuffer preserving GPU, the dirty region
+        //   has already been expanded as needed, so the code is correct
+        //   there too.
+        //
+        // However, the content of the framebuffer cannot be trusted when
+        // we switch to/from FB/OVERLAY, in which case we need to
+        // expand the dirty region to those areas too.
+        //
+        // Note also that there is a special case when switching from
+        // "no layers in FB" to "some layers in FB", where we need to redraw
+        // the entire FB, since some areas might contain uninitialized
+        // data.
+        //
+        // Also we want to make sure to not clear areas that belong to
+        // layers above that won't redraw (we would just erasing them),
+        // that is, we can't erase anything outside the dirty region.
+
+        Region transparent;
+
+        if (!fbLayerCount && hwc.getLayerCount(HWC_FRAMEBUFFER)) {
+            transparent.set(hw.getBounds());
+            dirtyInOut = transparent;
+        } else {
             for (size_t i=0 ; i<count ; i++) {
-                if (cur[i].hints & HWC_HINT_CLEAR_FB) {
-                    const sp<LayerBase>& layer(layers[i]);
-                    if (layer->isOpaque()) {
-                        transparent.orSelf(layer->visibleRegionScreen);
-                    }
+                const sp<LayerBase>& layer(layers[i]);
+                if ((cur[i].hints & HWC_HINT_CLEAR_FB) && layer->isOpaque()) {
+                    transparent.orSelf(layer->visibleRegionScreen);
                 }
+                bool isOverlay = (cur[i].compositionType != HWC_FRAMEBUFFER);
+                if (isOverlay != layer->isOverlay()) {
+                    // we transitioned to/from overlay, so add this layer
+                    // to the dirty region so the framebuffer can be either
+                    // cleared or redrawn.
+                    dirtyInOut.orSelf(layer->visibleRegionScreen);
+                }
+                layer->setOverlay(isOverlay);
             }
+            // don't erase stuff outside the dirty region
+            transparent.andSelf(dirtyInOut);
+        }
 
-            /*
-             *  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);
-                }
+        /*
+         *  clear the area of the FB that need to be transparent
+         */
+        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);
             }
         }
     }
+}
 
+void SurfaceFlinger::composeSurfaces(const Region& dirty)
+{
+    if (UNLIKELY(!mWormholeRegion.isEmpty())) {
+        // should never happen unless the window manager has a bug
+        // draw something...
+        drawWormhole();
+    }
+
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    HWComposer& hwc(hw.getHwComposer());
+    hwc_layer_t* const cur(hwc.getLayers());
 
     /*
      * and then, render the layers targeted at the framebuffer
      */
+    const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+    size_t count = layers.size();
     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;
-            }
+        if (cur && (cur[i].compositionType != HWC_FRAMEBUFFER)) {
+            continue;
         }
-
         const sp<LayerBase>& layer(layers[i]);
         const Region clip(dirty.intersect(layer->visibleRegionScreen));
         if (!clip.isEmpty()) {
@@ -990,7 +1018,7 @@
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     const uint32_t flags = hw.getFlags();
     const int32_t height = hw.getHeight();
-    if (mInvalidRegion.isEmpty()) {
+    if (mSwapRegion.isEmpty()) {
         return;
     }
 
@@ -1027,7 +1055,7 @@
         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
     }
 
-    hw.flip(mInvalidRegion);
+    hw.flip(mSwapRegion);
 
     if (mDebugRegion > 1)
         usleep(mDebugRegion * 1000);
@@ -1597,7 +1625,7 @@
                 hwc.initCheck()==NO_ERROR ? "present" : "not present",
                 (mDebugDisableHWC || mDebugRegion) ? "disabled" : "enabled");
         result.append(buffer);
-        hwc.dump(result, buffer, SIZE);
+        hwc.dump(result, buffer, SIZE, mVisibleLayersSortedByZ);
 
         /*
          * Dump gralloc state
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 5f8eb08..d7f005f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -277,6 +277,7 @@
             void        handleWorkList();
             void        handleRepaint();
             void        postFramebuffer();
+            void        setupHardwareComposer(Region& dirtyInOut);
             void        composeSurfaces(const Region& dirty);
             void        repaintEverything();
 
@@ -357,7 +358,7 @@
                 State                       mDrawingState;
                 Region                      mDirtyRegion;
                 Region                      mDirtyRegionRemovedLayer;
-                Region                      mInvalidRegion;
+                Region                      mSwapRegion;
                 Region                      mWormholeRegion;
                 bool                        mVisibleRegionsDirty;
                 bool                        mHwWorkListDirty;