am a6dbfdd3: Add a sprite controller. (DO NOT MERGE)

* commit 'a6dbfdd3a858aac52cc87f80f91e8eef7d613605':
  Add a sprite controller. (DO NOT MERGE)
diff --git a/services/input/Android.mk b/services/input/Android.mk
index d7b61fc..58b5318 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -22,7 +22,9 @@
     InputManager.cpp \
     InputReader.cpp \
     InputWindow.cpp \
-    PointerController.cpp
+    PointerController.cpp \
+    SpotController.cpp \
+    SpriteController.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index cf9b13d..9ed1391 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -20,6 +20,7 @@
 #include "EventHub.h"
 #include "InputDispatcher.h"
 #include "PointerController.h"
+#include "SpotController.h"
 
 #include <ui/Input.h>
 #include <ui/DisplayInfo.h>
@@ -89,6 +90,9 @@
 
     /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0;
+
+    /* Gets a spot controller associated with the specified touch pad device. */
+    virtual sp<SpotControllerInterface> obtainSpotController(int32_t deviceId) = 0;
 };
 
 
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index a4ee295..15effb7 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -49,8 +49,11 @@
 static const float FADE_DECAY_PER_FRAME = float(FADE_FRAME_INTERVAL) / FADE_DURATION;
 
 
-PointerController::PointerController(const sp<Looper>& looper, int32_t pointerLayer) :
-        mLooper(looper), mPointerLayer(pointerLayer) {
+PointerController::PointerController(const sp<Looper>& looper,
+        const sp<SpriteController>& spriteController) :
+        mLooper(looper), mSpriteController(spriteController) {
+    mHandler = new WeakMessageHandler(this);
+
     AutoMutex _l(mLock);
 
     mLocked.displayWidth = -1;
@@ -61,34 +64,20 @@
     mLocked.pointerY = 0;
     mLocked.buttonState = 0;
 
-    mLocked.iconBitmap = NULL;
-    mLocked.iconHotSpotX = 0;
-    mLocked.iconHotSpotY = 0;
-
     mLocked.fadeAlpha = 1;
     mLocked.inactivityFadeDelay = INACTIVITY_FADE_DELAY_NORMAL;
 
-    mLocked.wantVisible = false;
     mLocked.visible = false;
-    mLocked.drawn = false;
 
-    mHandler = new WeakMessageHandler(this);
+    mLocked.sprite = mSpriteController->createSprite();
 }
 
 PointerController::~PointerController() {
     mLooper->removeMessages(mHandler);
 
-    if (mSurfaceControl != NULL) {
-        mSurfaceControl->clear();
-        mSurfaceControl.clear();
-    }
+    AutoMutex _l(mLock);
 
-    if (mSurfaceComposerClient != NULL) {
-        mSurfaceComposerClient->dispose();
-        mSurfaceComposerClient.clear();
-    }
-
-    delete mLocked.iconBitmap;
+    mLocked.sprite.clear();
 }
 
 bool PointerController::getBounds(float* outMinX, float* outMinY,
@@ -214,75 +203,11 @@
 }
 
 void PointerController::updateLocked() {
-    bool wantVisibleAndHavePointerIcon = mLocked.wantVisible && mLocked.iconBitmap;
-
-    if (wantVisibleAndHavePointerIcon) {
-        // Want the pointer to be visible.
-        // Ensure the surface is created and drawn.
-        if (!createSurfaceIfNeededLocked() || !drawPointerIfNeededLocked()) {
-            return;
-        }
-    } else {
-        // Don't want the pointer to be visible.
-        // If it is not visible then we are done.
-        if (mSurfaceControl == NULL || !mLocked.visible) {
-            return;
-        }
-    }
-
-    status_t status = mSurfaceComposerClient->openTransaction();
-    if (status) {
-        LOGE("Error opening surface transaction to update pointer surface.");
-        return;
-    }
-
-    if (wantVisibleAndHavePointerIcon) {
-        status = mSurfaceControl->setPosition(
-                mLocked.pointerX - mLocked.iconHotSpotX,
-                mLocked.pointerY - mLocked.iconHotSpotY);
-        if (status) {
-            LOGE("Error %d moving pointer surface.", status);
-            goto CloseTransaction;
-        }
-
-        status = mSurfaceControl->setAlpha(mLocked.fadeAlpha);
-        if (status) {
-            LOGE("Error %d setting pointer surface alpha.", status);
-            goto CloseTransaction;
-        }
-
-        if (!mLocked.visible) {
-            status = mSurfaceControl->setLayer(mPointerLayer);
-            if (status) {
-                LOGE("Error %d setting pointer surface layer.", status);
-                goto CloseTransaction;
-            }
-
-            status = mSurfaceControl->show(mPointerLayer);
-            if (status) {
-                LOGE("Error %d showing pointer surface.", status);
-                goto CloseTransaction;
-            }
-
-            mLocked.visible = true;
-        }
-    } else {
-        if (mLocked.visible) {
-            status = mSurfaceControl->hide();
-            if (status) {
-                LOGE("Error %d hiding pointer surface.", status);
-                goto CloseTransaction;
-            }
-
-            mLocked.visible = false;
-        }
-    }
-
-CloseTransaction:
-    status = mSurfaceComposerClient->closeTransaction();
-    if (status) {
-        LOGE("Error closing surface transaction to update pointer surface.");
-    }
+    mLocked.sprite->openTransaction();
+    mLocked.sprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+    mLocked.sprite->setAlpha(mLocked.fadeAlpha);
+    mLocked.sprite->setVisible(mLocked.visible);
+    mLocked.sprite->closeTransaction();
 }
 
 void PointerController::setDisplaySize(int32_t width, int32_t height) {
@@ -339,7 +264,7 @@
         case DISPLAY_ORIENTATION_90:
             temp = x;
             x = y;
-            y = mLocked.displayWidth - x;
+            y = mLocked.displayWidth - temp;
             break;
         case DISPLAY_ORIENTATION_180:
             x = mLocked.displayWidth - x;
@@ -365,106 +290,7 @@
 void PointerController::setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) {
     AutoMutex _l(mLock);
 
-    if (mLocked.iconBitmap) {
-        delete mLocked.iconBitmap;
-        mLocked.iconBitmap = NULL;
-    }
-
-    if (bitmap) {
-        mLocked.iconBitmap = new SkBitmap();
-        bitmap->copyTo(mLocked.iconBitmap, SkBitmap::kARGB_8888_Config);
-    }
-
-    mLocked.iconHotSpotX = hotSpotX;
-    mLocked.iconHotSpotY = hotSpotY;
-    mLocked.drawn = false;
-}
-
-bool PointerController::createSurfaceIfNeededLocked() {
-    if (!mLocked.iconBitmap) {
-        // If we don't have a pointer icon, then no point allocating a surface now.
-        return false;
-    }
-
-    if (mSurfaceComposerClient == NULL) {
-        mSurfaceComposerClient = new SurfaceComposerClient();
-    }
-
-    if (mSurfaceControl == NULL) {
-        mSurfaceControl = mSurfaceComposerClient->createSurface(getpid(),
-                String8("Pointer Icon"), 0,
-                mLocked.iconBitmap->width(), mLocked.iconBitmap->height(),
-                PIXEL_FORMAT_RGBA_8888);
-        if (mSurfaceControl == NULL) {
-            LOGE("Error creating pointer surface.");
-            return false;
-        }
-    }
-    return true;
-}
-
-bool PointerController::drawPointerIfNeededLocked() {
-    if (!mLocked.drawn) {
-        if (!mLocked.iconBitmap) {
-            return false;
-        }
-
-        if (!resizeSurfaceLocked(mLocked.iconBitmap->width(), mLocked.iconBitmap->height())) {
-            return false;
-        }
-
-        sp<Surface> surface = mSurfaceControl->getSurface();
-
-        Surface::SurfaceInfo surfaceInfo;
-        status_t status = surface->lock(&surfaceInfo);
-        if (status) {
-            LOGE("Error %d locking pointer surface before drawing.", status);
-            return false;
-        }
-
-        SkBitmap surfaceBitmap;
-        ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format);
-        surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config, surfaceInfo.w, surfaceInfo.h, bpr);
-        surfaceBitmap.setPixels(surfaceInfo.bits);
-
-        SkCanvas surfaceCanvas;
-        surfaceCanvas.setBitmapDevice(surfaceBitmap);
-
-        SkPaint paint;
-        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-        surfaceCanvas.drawBitmap(*mLocked.iconBitmap, 0, 0, &paint);
-
-        status = surface->unlockAndPost();
-        if (status) {
-            LOGE("Error %d unlocking pointer surface after drawing.", status);
-            return false;
-        }
-    }
-
-    mLocked.drawn = true;
-    return true;
-}
-
-bool PointerController::resizeSurfaceLocked(int32_t width, int32_t height) {
-    status_t status = mSurfaceComposerClient->openTransaction();
-    if (status) {
-        LOGE("Error opening surface transaction to resize pointer surface.");
-        return false;
-    }
-
-    status = mSurfaceControl->setSize(width, height);
-    if (status) {
-        LOGE("Error %d setting pointer surface size.", status);
-        return false;
-    }
-
-    status = mSurfaceComposerClient->closeTransaction();
-    if (status) {
-        LOGE("Error closing surface transaction to resize pointer surface.");
-        return false;
-    }
-
-    return true;
+    mLocked.sprite->setBitmap(bitmap, hotSpotX, hotSpotY);
 }
 
 void PointerController::handleMessage(const Message& message) {
@@ -481,7 +307,7 @@
     sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
 
     if (isFadingLocked()) {
-        mLocked.wantVisible = true;
+        mLocked.visible = true;
         mLocked.fadeAlpha = 1;
         return true; // update required to effect the unfade
     }
@@ -501,11 +327,11 @@
 }
 
 void PointerController::fadeStepLocked() {
-    if (mLocked.wantVisible) {
+    if (mLocked.visible) {
         mLocked.fadeAlpha -= FADE_DECAY_PER_FRAME;
         if (mLocked.fadeAlpha < 0) {
             mLocked.fadeAlpha = 0;
-            mLocked.wantVisible = false;
+            mLocked.visible = false;
         } else {
             sendFadeStepMessageDelayedLocked(FADE_FRAME_INTERVAL);
         }
@@ -514,7 +340,7 @@
 }
 
 bool PointerController::isFadingLocked() {
-    return !mLocked.wantVisible || mLocked.fadeAlpha != 1;
+    return !mLocked.visible || mLocked.fadeAlpha != 1;
 }
 
 nsecs_t PointerController::getInactivityFadeDelayTimeLocked() {
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index e1dab5c..d467a5a 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -17,16 +17,14 @@
 #ifndef _UI_POINTER_CONTROLLER_H
 #define _UI_POINTER_CONTROLLER_H
 
+#include "SpriteController.h"
+
 #include <ui/DisplayInfo.h>
 #include <ui/Input.h>
 #include <utils/RefBase.h>
 #include <utils/Looper.h>
 #include <utils/String8.h>
 
-#include <surfaceflinger/Surface.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
-#include <surfaceflinger/ISurfaceComposer.h>
-
 #include <SkBitmap.h>
 
 namespace android {
@@ -86,7 +84,7 @@
         INACTIVITY_FADE_DELAY_SHORT = 1,
     };
 
-    PointerController(const sp<Looper>& looper, int32_t pointerLayer);
+    PointerController(const sp<Looper>& looper, const sp<SpriteController>& spriteController);
 
     virtual bool getBounds(float* outMinX, float* outMinY,
             float* outMaxX, float* outMaxY) const;
@@ -111,9 +109,8 @@
     mutable Mutex mLock;
 
     sp<Looper> mLooper;
-    int32_t mPointerLayer;
-    sp<SurfaceComposerClient> mSurfaceComposerClient;
-    sp<SurfaceControl> mSurfaceControl;
+    sp<SpriteController> mSpriteController;
+    sp<WeakMessageHandler> mHandler;
 
     struct Locked {
         int32_t displayWidth;
@@ -124,26 +121,17 @@
         float pointerY;
         uint32_t buttonState;
 
-        SkBitmap* iconBitmap;
-        float iconHotSpotX;
-        float iconHotSpotY;
-
         float fadeAlpha;
         InactivityFadeDelay inactivityFadeDelay;
 
-        bool wantVisible;
         bool visible;
-        bool drawn;
-    } mLocked;
 
-    sp<WeakMessageHandler> mHandler;
+        sp<Sprite> sprite;
+    } mLocked;
 
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     void setPositionLocked(float x, float y);
     void updateLocked();
-    bool createSurfaceIfNeededLocked();
-    bool drawPointerIfNeededLocked();
-    bool resizeSurfaceLocked(int32_t width, int32_t height);
 
     void handleMessage(const Message& message);
     bool unfadeBeforeUpdateLocked();
diff --git a/services/input/SpotController.cpp b/services/input/SpotController.cpp
new file mode 100644
index 0000000..dffad81
--- /dev/null
+++ b/services/input/SpotController.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SpotController"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about spot updates
+#define DEBUG_SPOT_UPDATES 0
+
+#include "SpotController.h"
+
+#include <cutils/log.h>
+
+namespace android {
+
+// --- SpotController ---
+
+SpotController::SpotController(const sp<Looper>& looper,
+        const sp<SpriteController>& spriteController) :
+        mLooper(looper), mSpriteController(spriteController) {
+    mHandler = new WeakMessageHandler(this);
+}
+
+SpotController::~SpotController() {
+    mLooper->removeMessages(mHandler);
+}
+
+void SpotController:: handleMessage(const Message& message) {
+}
+
+} // namespace android
diff --git a/services/input/SpotController.h b/services/input/SpotController.h
new file mode 100644
index 0000000..1d091d7
--- /dev/null
+++ b/services/input/SpotController.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_SPOT_CONTROLLER_H
+#define _UI_SPOT_CONTROLLER_H
+
+#include "SpriteController.h"
+
+#include <utils/RefBase.h>
+#include <utils/Looper.h>
+
+#include <SkBitmap.h>
+
+namespace android {
+
+/*
+ * Interface for displaying spots on screen that visually represent the positions
+ * of fingers on a touch pad.
+ *
+ * The spot controller is responsible for providing synchronization and for tracking
+ * display orientation changes if needed.
+ */
+class SpotControllerInterface : public virtual RefBase {
+protected:
+    SpotControllerInterface() { }
+    virtual ~SpotControllerInterface() { }
+
+public:
+
+};
+
+
+/*
+ * Sprite-based spot controller implementation.
+ */
+class SpotController : public SpotControllerInterface, public MessageHandler {
+protected:
+    virtual ~SpotController();
+
+public:
+    SpotController(const sp<Looper>& looper, const sp<SpriteController>& spriteController);
+
+private:
+    mutable Mutex mLock;
+
+    sp<Looper> mLooper;
+    sp<SpriteController> mSpriteController;
+    sp<WeakMessageHandler> mHandler;
+
+    struct Locked {
+    } mLocked;
+
+    void handleMessage(const Message& message);
+};
+
+} // namespace android
+
+#endif // _UI_SPOT_CONTROLLER_H
diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp
new file mode 100644
index 0000000..c6d4390f
--- /dev/null
+++ b/services/input/SpriteController.cpp
@@ -0,0 +1,472 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Sprites"
+
+//#define LOG_NDEBUG 0
+
+#include "SpriteController.h"
+
+#include <cutils/log.h>
+#include <utils/String8.h>
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
+#include <SkXfermode.h>
+
+namespace android {
+
+// --- SpriteController ---
+
+SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLayer) :
+        mLooper(looper), mOverlayLayer(overlayLayer) {
+    mHandler = new WeakMessageHandler(this);
+}
+
+SpriteController::~SpriteController() {
+    mLooper->removeMessages(mHandler);
+
+    if (mSurfaceComposerClient != NULL) {
+        mSurfaceComposerClient->dispose();
+        mSurfaceComposerClient.clear();
+    }
+}
+
+sp<Sprite> SpriteController::createSprite() {
+    return new SpriteImpl(this);
+}
+
+void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
+    bool wasEmpty = mInvalidatedSprites.isEmpty();
+    mInvalidatedSprites.push(sprite);
+    if (wasEmpty) {
+        mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES));
+    }
+}
+
+void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl) {
+    bool wasEmpty = mDisposedSurfaces.isEmpty();
+    mDisposedSurfaces.push(surfaceControl);
+    if (wasEmpty) {
+        mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES));
+    }
+}
+
+void SpriteController::handleMessage(const Message& message) {
+    switch (message.what) {
+    case MSG_UPDATE_SPRITES:
+        doUpdateSprites();
+        break;
+    case MSG_DISPOSE_SURFACES:
+        doDisposeSurfaces();
+        break;
+    }
+}
+
+void SpriteController::doUpdateSprites() {
+    // Collect information about sprite updates.
+    // Each sprite update record includes a reference to its associated sprite so we can
+    // be certain the sprites will not be deleted while this function runs.  Sprites
+    // may invalidate themselves again during this time but we will handle those changes
+    // in the next iteration.
+    Vector<SpriteUpdate> updates;
+    size_t numSprites;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        numSprites = mInvalidatedSprites.size();
+        for (size_t i = 0; i < numSprites; i++) {
+            const sp<SpriteImpl>& sprite = mInvalidatedSprites.itemAt(i);
+
+            updates.push(SpriteUpdate(sprite, sprite->getStateLocked()));
+            sprite->resetDirtyLocked();
+        }
+        mInvalidatedSprites.clear();
+    } // release lock
+
+    // Create missing surfaces.
+    bool surfaceChanged = false;
+    for (size_t i = 0; i < numSprites; i++) {
+        SpriteUpdate& update = updates.editItemAt(i);
+
+        if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
+            update.state.surfaceWidth = update.state.bitmap.width();
+            update.state.surfaceHeight = update.state.bitmap.height();
+            update.state.surfaceDrawn = false;
+            update.state.surfaceVisible = false;
+            update.state.surfaceControl = obtainSurface(
+                    update.state.surfaceWidth, update.state.surfaceHeight);
+            if (update.state.surfaceControl != NULL) {
+                update.surfaceChanged = surfaceChanged = true;
+            }
+        }
+    }
+
+    // Resize sprites if needed, inside a global transaction.
+    bool haveGlobalTransaction = false;
+    for (size_t i = 0; i < numSprites; i++) {
+        SpriteUpdate& update = updates.editItemAt(i);
+
+        if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
+            int32_t desiredWidth = update.state.bitmap.width();
+            int32_t desiredHeight = update.state.bitmap.height();
+            if (update.state.surfaceWidth < desiredWidth
+                    || update.state.surfaceHeight < desiredHeight) {
+                if (!haveGlobalTransaction) {
+                    SurfaceComposerClient::openGlobalTransaction();
+                    haveGlobalTransaction = true;
+                }
+
+                status_t status = update.state.surfaceControl->setSize(desiredWidth, desiredHeight);
+                if (status) {
+                    LOGE("Error %d resizing sprite surface from %dx%d to %dx%d",
+                            status, update.state.surfaceWidth, update.state.surfaceHeight,
+                            desiredWidth, desiredHeight);
+                } else {
+                    update.state.surfaceWidth = desiredWidth;
+                    update.state.surfaceHeight = desiredHeight;
+                    update.state.surfaceDrawn = false;
+                    update.surfaceChanged = surfaceChanged = true;
+
+                    if (update.state.surfaceVisible) {
+                        status = update.state.surfaceControl->hide();
+                        if (status) {
+                            LOGE("Error %d hiding sprite surface after resize.", status);
+                        } else {
+                            update.state.surfaceVisible = false;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    if (haveGlobalTransaction) {
+        SurfaceComposerClient::closeGlobalTransaction();
+    }
+
+    // Redraw sprites if needed.
+    for (size_t i = 0; i < numSprites; i++) {
+        SpriteUpdate& update = updates.editItemAt(i);
+
+        if ((update.state.dirty & DIRTY_BITMAP) && update.state.surfaceDrawn) {
+            update.state.surfaceDrawn = false;
+            update.surfaceChanged = surfaceChanged = true;
+        }
+
+        if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
+                && update.state.wantSurfaceVisible()) {
+            sp<Surface> surface = update.state.surfaceControl->getSurface();
+            Surface::SurfaceInfo surfaceInfo;
+            status_t status = surface->lock(&surfaceInfo);
+            if (status) {
+                LOGE("Error %d locking sprite surface before drawing.", status);
+            } else {
+                SkBitmap surfaceBitmap;
+                ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format);
+                surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
+                        surfaceInfo.w, surfaceInfo.h, bpr);
+                surfaceBitmap.setPixels(surfaceInfo.bits);
+
+                SkCanvas surfaceCanvas;
+                surfaceCanvas.setBitmapDevice(surfaceBitmap);
+
+                SkPaint paint;
+                paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+                surfaceCanvas.drawBitmap(update.state.bitmap, 0, 0, &paint);
+
+                if (surfaceInfo.w > uint32_t(update.state.bitmap.width())) {
+                    paint.setColor(0); // transparent fill color
+                    surfaceCanvas.drawRectCoords(update.state.bitmap.width(), 0,
+                            surfaceInfo.w, update.state.bitmap.height(), paint);
+                }
+                if (surfaceInfo.h > uint32_t(update.state.bitmap.height())) {
+                    paint.setColor(0); // transparent fill color
+                    surfaceCanvas.drawRectCoords(0, update.state.bitmap.height(),
+                            surfaceInfo.w, surfaceInfo.h, paint);
+                }
+
+                status = surface->unlockAndPost();
+                if (status) {
+                    LOGE("Error %d unlocking and posting sprite surface after drawing.", status);
+                } else {
+                    update.state.surfaceDrawn = true;
+                    update.surfaceChanged = surfaceChanged = true;
+                }
+            }
+        }
+    }
+
+    // Set sprite surface properties and make them visible.
+    bool haveTransaction = false;
+    for (size_t i = 0; i < numSprites; i++) {
+        SpriteUpdate& update = updates.editItemAt(i);
+
+        bool wantSurfaceVisibleAndDrawn = update.state.wantSurfaceVisible()
+                && update.state.surfaceDrawn;
+        bool becomingVisible = wantSurfaceVisibleAndDrawn && !update.state.surfaceVisible;
+        bool becomingHidden = !wantSurfaceVisibleAndDrawn && update.state.surfaceVisible;
+        if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
+                || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
+                        | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
+                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
+            status_t status;
+            if (!haveTransaction) {
+                status = mSurfaceComposerClient->openTransaction();
+                if (status) {
+                    LOGE("Error %d opening transation to update sprite surface.", status);
+                    break;
+                }
+                haveTransaction = true;
+            }
+
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible || (update.state.dirty & DIRTY_ALPHA))) {
+                status = update.state.surfaceControl->setAlpha(update.state.alpha);
+                if (status) {
+                    LOGE("Error %d setting sprite surface alpha.", status);
+                }
+            }
+
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible || (update.state.dirty & (DIRTY_POSITION
+                            | DIRTY_HOTSPOT)))) {
+                status = update.state.surfaceControl->setPosition(
+                        update.state.positionX - update.state.hotSpotX,
+                        update.state.positionY - update.state.hotSpotY);
+                if (status) {
+                    LOGE("Error %d setting sprite surface position.", status);
+                }
+            }
+
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible
+                            || (update.state.dirty & DIRTY_TRANSFORMATION_MATRIX))) {
+                status = update.state.surfaceControl->setMatrix(
+                        update.state.transformationMatrix.dsdx,
+                        update.state.transformationMatrix.dtdx,
+                        update.state.transformationMatrix.dsdy,
+                        update.state.transformationMatrix.dtdy);
+                if (status) {
+                    LOGE("Error %d setting sprite surface transformation matrix.", status);
+                }
+            }
+
+            int32_t surfaceLayer = mOverlayLayer + update.state.layer;
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
+                status = update.state.surfaceControl->setLayer(surfaceLayer);
+                if (status) {
+                    LOGE("Error %d setting sprite surface layer.", status);
+                }
+            }
+
+            if (becomingVisible) {
+                status = update.state.surfaceControl->show(surfaceLayer);
+                if (status) {
+                    LOGE("Error %d showing sprite surface.", status);
+                } else {
+                    update.state.surfaceVisible = true;
+                    update.surfaceChanged = surfaceChanged = true;
+                }
+            } else if (becomingHidden) {
+                status = update.state.surfaceControl->hide();
+                if (status) {
+                    LOGE("Error %d hiding sprite surface.", status);
+                } else {
+                    update.state.surfaceVisible = false;
+                    update.surfaceChanged = surfaceChanged = true;
+                }
+            }
+        }
+    }
+
+    if (haveTransaction) {
+        status_t status = mSurfaceComposerClient->closeTransaction();
+        if (status) {
+            LOGE("Error %d closing transaction to update sprite surface.", status);
+        }
+    }
+
+    // If any surfaces were changed, write back the new surface properties to the sprites.
+    if (surfaceChanged) { // acquire lock
+        AutoMutex _l(mLock);
+
+        for (size_t i = 0; i < numSprites; i++) {
+            const SpriteUpdate& update = updates.itemAt(i);
+
+            if (update.surfaceChanged) {
+                update.sprite->setSurfaceLocked(update.state.surfaceControl,
+                        update.state.surfaceWidth, update.state.surfaceHeight,
+                        update.state.surfaceDrawn, update.state.surfaceVisible);
+            }
+        }
+    } // release lock
+
+    // Clear the sprite update vector outside the lock.  It is very important that
+    // we do not clear sprite references inside the lock since we could be releasing
+    // the last remaining reference to the sprite here which would result in the
+    // sprite being deleted and the lock being reacquired by the sprite destructor
+    // while already held.
+    updates.clear();
+}
+
+void SpriteController::doDisposeSurfaces() {
+    // Collect disposed surfaces.
+    Vector<sp<SurfaceControl> > disposedSurfaces;
+    { // acquire lock
+        disposedSurfaces = mDisposedSurfaces;
+        mDisposedSurfaces.clear();
+    } // release lock
+
+    // Release the last reference to each surface outside of the lock.
+    // We don't want the surfaces to be deleted while we are holding our lock.
+    disposedSurfaces.clear();
+}
+
+void SpriteController::ensureSurfaceComposerClient() {
+    if (mSurfaceComposerClient == NULL) {
+        mSurfaceComposerClient = new SurfaceComposerClient();
+    }
+}
+
+sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height) {
+    ensureSurfaceComposerClient();
+
+    sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
+            getpid(), String8("Sprite"), 0, width, height, PIXEL_FORMAT_RGBA_8888);
+    if (surfaceControl == NULL) {
+        LOGE("Error creating sprite surface.");
+        return NULL;
+    }
+    return surfaceControl;
+}
+
+
+// --- SpriteController::SpriteImpl ---
+
+SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) :
+        mController(controller), mTransactionNestingCount(0) {
+}
+
+SpriteController::SpriteImpl::~SpriteImpl() {
+    AutoMutex _m(mController->mLock);
+
+    // Let the controller take care of deleting the last reference to sprite
+    // surfaces so that we do not block the caller on an IPC here.
+    if (mState.surfaceControl != NULL) {
+        mController->disposeSurfaceLocked(mState.surfaceControl);
+        mState.surfaceControl.clear();
+    }
+}
+
+void SpriteController::SpriteImpl::setBitmap(const SkBitmap* bitmap,
+        float hotSpotX, float hotSpotY) {
+    AutoMutex _l(mController->mLock);
+
+    if (bitmap) {
+        bitmap->copyTo(&mState.bitmap, SkBitmap::kARGB_8888_Config);
+    } else {
+        mState.bitmap.reset();
+    }
+
+    uint32_t dirty = DIRTY_BITMAP;
+    if (mState.hotSpotX != hotSpotX || mState.hotSpotY != hotSpotY) {
+        mState.hotSpotX = hotSpotX;
+        mState.hotSpotY = hotSpotY;
+        dirty |= DIRTY_HOTSPOT;
+    }
+
+    invalidateLocked(dirty);
+}
+
+void SpriteController::SpriteImpl::setVisible(bool visible) {
+    AutoMutex _l(mController->mLock);
+
+    if (mState.visible != visible) {
+        mState.visible = visible;
+        invalidateLocked(DIRTY_VISIBILITY);
+    }
+}
+
+void SpriteController::SpriteImpl::setPosition(float x, float y) {
+    AutoMutex _l(mController->mLock);
+
+    if (mState.positionX != x || mState.positionY != y) {
+        mState.positionX = x;
+        mState.positionY = y;
+        invalidateLocked(DIRTY_POSITION);
+    }
+}
+
+void SpriteController::SpriteImpl::setLayer(int32_t layer) {
+    AutoMutex _l(mController->mLock);
+
+    if (mState.layer != layer) {
+        mState.layer = layer;
+        invalidateLocked(DIRTY_LAYER);
+    }
+}
+
+void SpriteController::SpriteImpl::setAlpha(float alpha) {
+    AutoMutex _l(mController->mLock);
+
+    if (mState.alpha != alpha) {
+        mState.alpha = alpha;
+        invalidateLocked(DIRTY_ALPHA);
+    }
+}
+
+void SpriteController::SpriteImpl::setTransformationMatrix(
+        const SpriteTransformationMatrix& matrix) {
+    AutoMutex _l(mController->mLock);
+
+    if (mState.transformationMatrix != matrix) {
+        mState.transformationMatrix = matrix;
+        invalidateLocked(DIRTY_TRANSFORMATION_MATRIX);
+    }
+}
+
+void SpriteController::SpriteImpl::openTransaction() {
+    AutoMutex _l(mController->mLock);
+
+    mTransactionNestingCount += 1;
+}
+
+void SpriteController::SpriteImpl::closeTransaction() {
+    AutoMutex _l(mController->mLock);
+
+    LOG_ALWAYS_FATAL_IF(mTransactionNestingCount == 0,
+            "Sprite closeTransaction() called but there is no open sprite transaction");
+
+    mTransactionNestingCount -= 1;
+    if (mTransactionNestingCount == 0 && mState.dirty) {
+        mController->invalidateSpriteLocked(this);
+    }
+}
+
+void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
+    if (mTransactionNestingCount > 0) {
+        bool wasDirty = mState.dirty;
+        mState.dirty |= dirty;
+        if (!wasDirty) {
+            mController->invalidateSpriteLocked(this);
+        }
+    }
+}
+
+} // namespace android
diff --git a/services/input/SpriteController.h b/services/input/SpriteController.h
new file mode 100644
index 0000000..27afb5e
--- /dev/null
+++ b/services/input/SpriteController.h
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_SPRITES_H
+#define _UI_SPRITES_H
+
+#include <utils/RefBase.h>
+#include <utils/Looper.h>
+
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <SkBitmap.h>
+
+namespace android {
+
+/*
+ * Transformation matrix for a sprite.
+ */
+struct SpriteTransformationMatrix {
+    inline SpriteTransformationMatrix() : dsdx(1.0f), dtdx(0.0f), dsdy(0.0f), dtdy(1.0f) { }
+
+    float dsdx;
+    float dtdx;
+    float dsdy;
+    float dtdy;
+
+    inline bool operator== (const SpriteTransformationMatrix& other) {
+        return dsdx == other.dsdx
+                && dtdx == other.dtdx
+                && dsdy == other.dsdy
+                && dtdy == other.dtdy;
+    }
+
+    inline bool operator!= (const SpriteTransformationMatrix& other) {
+        return !(*this == other);
+    }
+};
+
+/*
+ * A sprite is a simple graphical object that is displayed on-screen above other layers.
+ * The basic sprite class is an interface.
+ * The implementation is provided by the sprite controller.
+ */
+class Sprite : public RefBase {
+protected:
+    Sprite() { }
+    virtual ~Sprite() { }
+
+public:
+    /* Sets the bitmap that is drawn by the sprite.
+     * The sprite retains a copy of the bitmap for subsequent rendering. */
+    virtual void setBitmap(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) = 0;
+
+    /* Sets whether the sprite is visible. */
+    virtual void setVisible(bool visible) = 0;
+
+    /* Sets the sprite position on screen, relative to the sprite's hot spot. */
+    virtual void setPosition(float x, float y) = 0;
+
+    /* Sets the layer of the sprite, relative to the system sprite overlay layer.
+     * Layer 0 is the overlay layer, > 0 appear above this layer. */
+    virtual void setLayer(int32_t layer) = 0;
+
+    /* Sets the sprite alpha blend ratio between 0.0 and 1.0. */
+    virtual void setAlpha(float alpha) = 0;
+
+    /* Sets the sprite transformation matrix. */
+    virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
+
+    /* Opens or closes a transaction to perform a batch of sprite updates as part of
+     * a single operation such as setPosition and setAlpha.  It is not necessary to
+     * open a transaction when updating a single property.
+     * Calls to openTransaction() nest and must be matched by an equal number
+     * of calls to closeTransaction(). */
+    virtual void openTransaction() = 0;
+    virtual void closeTransaction() = 0;
+};
+
+/*
+ * Displays sprites on the screen.
+ *
+ * This interface is used by PointerController and SpotController to draw pointers or
+ * spot representations of fingers.  It is not intended for general purpose use
+ * by other components.
+ *
+ * All sprite position updates and rendering is performed asynchronously.
+ *
+ * Clients are responsible for animating sprites by periodically updating their properties.
+ */
+class SpriteController : public MessageHandler {
+protected:
+    virtual ~SpriteController();
+
+public:
+    SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
+
+    /* Creates a new sprite, initially invisible. */
+    sp<Sprite> createSprite();
+
+private:
+    enum {
+        MSG_UPDATE_SPRITES,
+        MSG_DISPOSE_SURFACES,
+    };
+
+    enum {
+        DIRTY_BITMAP = 1 << 0,
+        DIRTY_ALPHA = 1 << 1,
+        DIRTY_POSITION = 1 << 2,
+        DIRTY_TRANSFORMATION_MATRIX = 1 << 3,
+        DIRTY_LAYER = 1 << 4,
+        DIRTY_VISIBILITY = 1 << 5,
+        DIRTY_HOTSPOT = 1 << 6,
+    };
+
+    /* Describes the state of a sprite.
+     * This structure is designed so that it can be copied during updates so that
+     * surfaces can be resized and redrawn without blocking the client by holding a lock
+     * on the sprites for a long time.
+     * Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */
+    struct SpriteState {
+        inline SpriteState() :
+                dirty(0), hotSpotX(0), hotSpotY(0), visible(false),
+                positionX(0), positionY(0), layer(0), alpha(1.0f),
+                surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
+        }
+
+        uint32_t dirty;
+
+        SkBitmap bitmap;
+        float hotSpotX;
+        float hotSpotY;
+        bool visible;
+        float positionX;
+        float positionY;
+        int32_t layer;
+        float alpha;
+        SpriteTransformationMatrix transformationMatrix;
+
+        sp<SurfaceControl> surfaceControl;
+        int32_t surfaceWidth;
+        int32_t surfaceHeight;
+        bool surfaceDrawn;
+        bool surfaceVisible;
+
+        inline bool wantSurfaceVisible() const {
+            return visible && alpha > 0.0f && !bitmap.isNull() && !bitmap.empty();
+        }
+    };
+
+    /* Client interface for a sprite.
+     * Requests acquire a lock on the controller, update local state and request the
+     * controller to invalidate the sprite.
+     * The real heavy lifting of creating, resizing and redrawing surfaces happens
+     * asynchronously with no locks held except in short critical section to copy
+     * the sprite state before the work and update the sprite surface control afterwards.
+     */
+    class SpriteImpl : public Sprite {
+    protected:
+        virtual ~SpriteImpl();
+
+    public:
+        SpriteImpl(const sp<SpriteController> controller);
+
+        virtual void setBitmap(const SkBitmap* bitmap, float hotSpotX, float hotSpotY);
+        virtual void setVisible(bool visible);
+        virtual void setPosition(float x, float y);
+        virtual void setLayer(int32_t layer);
+        virtual void setAlpha(float alpha);
+        virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
+        virtual void openTransaction();
+        virtual void closeTransaction();
+
+        inline const SpriteState& getStateLocked() const {
+            return mState;
+        }
+
+        inline void resetDirtyLocked() {
+            mState.dirty = 0;
+        }
+
+        inline void setSurfaceLocked(const sp<SurfaceControl>& surfaceControl,
+                int32_t width, int32_t height, bool drawn, bool visible) {
+            mState.surfaceControl = surfaceControl;
+            mState.surfaceWidth = width;
+            mState.surfaceHeight = height;
+            mState.surfaceDrawn = drawn;
+            mState.surfaceVisible = visible;
+        }
+
+    private:
+        sp<SpriteController> mController;
+
+        SpriteState mState; // guarded by mController->mLock
+        uint32_t mTransactionNestingCount; // guarded by mController->mLock
+
+        void invalidateLocked(uint32_t dirty);
+    };
+
+    /* Stores temporary information collected during the sprite update cycle. */
+    struct SpriteUpdate {
+        inline SpriteUpdate() : surfaceChanged(false) { }
+        inline SpriteUpdate(const sp<SpriteImpl> sprite, const SpriteState& state) :
+                sprite(sprite), state(state), surfaceChanged(false) {
+        }
+
+        sp<SpriteImpl> sprite;
+        SpriteState state;
+        bool surfaceChanged;
+    };
+
+    mutable Mutex mLock;
+
+    sp<Looper> mLooper;
+    const int32_t mOverlayLayer;
+    sp<WeakMessageHandler> mHandler;
+
+    sp<SurfaceComposerClient> mSurfaceComposerClient;
+
+    Vector<sp<SpriteImpl> > mInvalidatedSprites; // guarded by mLock
+    Vector<sp<SurfaceControl> > mDisposedSurfaces; // guarded by mLock
+
+    void invalidateSpriteLocked(const sp<SpriteImpl>& sprite);
+    void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl);
+
+    void handleMessage(const Message& message);
+    void doUpdateSprites();
+    void doDisposeSurfaces();
+
+    void ensureSurfaceComposerClient();
+    sp<SurfaceControl> obtainSurface(int32_t width, int32_t height);
+};
+
+} // namespace android
+
+#endif // _UI_SPRITES_H
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 60549c6..6feb2c7 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -192,6 +192,10 @@
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
         return mPointerControllers.valueFor(deviceId);
     }
+
+    virtual sp<SpotControllerInterface> obtainSpotController(int32_t device) {
+        return NULL;
+    }
 };
 
 
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 426197a..7985fab 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -36,6 +36,8 @@
 
 #include <input/InputManager.h>
 #include <input/PointerController.h>
+#include <input/SpotController.h>
+#include <input/SpriteController.h>
 
 #include <android_os_MessageQueue.h>
 #include <android_view_KeyEvent.h>
@@ -168,6 +170,7 @@
     virtual nsecs_t getVirtualKeyQuietTime();
     virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
     virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
+    virtual sp<SpotControllerInterface> obtainSpotController(int32_t deviceId);
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -217,12 +220,16 @@
         // System UI visibility.
         int32_t systemUiVisibility;
 
+        // Sprite controller singleton, created on first use.
+        sp<SpriteController> spriteController;
+
         // Pointer controller singleton, created and destroyed as needed.
         wp<PointerController> pointerController;
     } mLocked;
 
     void updateInactivityFadeDelayLocked(const sp<PointerController>& controller);
     void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
+    void ensureSpriteControllerLocked();
 
     // Power manager interactions.
     bool isScreenOn();
@@ -423,18 +430,15 @@
 
     sp<PointerController> controller = mLocked.pointerController.promote();
     if (controller == NULL) {
-        JNIEnv* env = jniEnv();
-        jint layer = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getPointerLayer);
-        if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
-            layer = -1;
-        }
+        ensureSpriteControllerLocked();
 
-        controller = new PointerController(mLooper, layer);
+        controller = new PointerController(mLooper, mLocked.spriteController);
         mLocked.pointerController = controller;
 
         controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight);
         controller->setDisplayOrientation(mLocked.displayOrientation);
 
+        JNIEnv* env = jniEnv();
         jobject iconObj = env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getPointerIcon);
         if (!checkAndClearExceptionFromCallback(env, "getPointerIcon") && iconObj) {
             jfloat iconHotSpotX = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotX);
@@ -455,6 +459,24 @@
     return controller;
 }
 
+sp<SpotControllerInterface> NativeInputManager::obtainSpotController(int32_t deviceId) {
+    AutoMutex _l(mLock);
+
+    ensureSpriteControllerLocked();
+    return new SpotController(mLooper, mLocked.spriteController);
+}
+
+void NativeInputManager::ensureSpriteControllerLocked() {
+    if (mLocked.spriteController == NULL) {
+        JNIEnv* env = jniEnv();
+        jint layer = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getPointerLayer);
+        if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
+            layer = -1;
+        }
+        mLocked.spriteController = new SpriteController(mLooper, layer);
+    }
+}
+
 void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode,
         int32_t switchValue, uint32_t policyFlags) {
 #if DEBUG_INPUT_DISPATCHER_POLICY