Mouse pointer integration.

Added support for loading the pointer icon from a resource.

Moved the system server related bits of the input manager out
of libui and into libinput since they do not need to be linked into
applications.

Change-Id: Iec11e0725b3add2b905c51f8ea2c3b4b0d1a2d67
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
index 457cbd4..e8afce3 100644
--- a/cmds/system_server/library/Android.mk
+++ b/cmds/system_server/library/Android.mk
@@ -21,6 +21,7 @@
 	libaudioflinger \
     libcameraservice \
     libmediaplayerservice \
+    libinput \
 	libutils \
 	libbinder \
 	libcutils
diff --git a/core/res/res/drawable-mdpi/pointericon_default.xml b/core/res/res/drawable-mdpi/pointericon_default.xml
new file mode 100644
index 0000000..b1357b6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointericon_default.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointericon_default_bitmap"
+    android:hotSpotX="2"
+    android:hotSpotY="2" />
diff --git a/core/res/res/drawable-mdpi/pointericon_default_bitmap.png b/core/res/res/drawable-mdpi/pointericon_default_bitmap.png
new file mode 100644
index 0000000..7e073a3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/pointericon_default_bitmap.png
Binary files differ
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 588b33b..890ac7c 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4708,4 +4708,13 @@
         <!-- Minimum space between the switch and caption text -->
         <attr name="switchPadding" format="dimension" />
     </declare-styleable>
+
+    <declare-styleable name="PointerIcon">
+        <!-- Drawable to use as the icon bitmap. -->
+        <attr name="bitmap" format="reference" />
+        <!-- X coordinate of the icon hot spot. -->
+        <attr name="hotSpotX" format="float" />
+        <!-- Y coordinate of the icon hot spot. -->
+        <attr name="hotSpotY" format="float" />
+    </declare-styleable>
 </resources>
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
index c419efe..edd28a6 100644
--- a/include/ui/DisplayInfo.h
+++ b/include/ui/DisplayInfo.h
@@ -37,6 +37,15 @@
     float               ydpi;
 };
 
+/* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
+enum {
+    DISPLAY_ORIENTATION_0 = 0,
+    DISPLAY_ORIENTATION_90 = 1,
+    DISPLAY_ORIENTATION_180 = 2,
+    DISPLAY_ORIENTATION_270 = 3
+};
+
+
 }; // namespace android
 
 #endif // ANDROID_COMPOSER_DISPLAY_INFO_H
diff --git a/include/ui/PointerController.h b/include/ui/PointerController.h
deleted file mode 100644
index 4db24e5..0000000
--- a/include/ui/PointerController.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UI_POINTER_CONTROLLER_H
-#define _UI_POINTER_CONTROLLER_H
-
-#include <utils/RefBase.h>
-
-namespace android {
-
-enum {
-    POINTER_BUTTON_1 = 1 << 0,
-};
-
-/**
- * Interface for tracking a single (mouse) pointer.
- *
- * The pointer controller is responsible for providing synchronization and for tracking
- * display orientation changes if needed.
- */
-class PointerControllerInterface : public virtual RefBase {
-protected:
-    PointerControllerInterface() { }
-    virtual ~PointerControllerInterface() { }
-
-public:
-    /* Gets the bounds of the region that the pointer can traverse.
-     * Returns true if the bounds are available. */
-    virtual bool getBounds(float* outMinX, float* outMinY,
-            float* outMaxX, float* outMaxY) const = 0;
-
-    /* Move the pointer. */
-    virtual void move(float deltaX, float deltaY) = 0;
-
-    /* Sets a mask that indicates which buttons are pressed. */
-    virtual void setButtonState(uint32_t buttonState) = 0;
-
-    /* Gets a mask that indicates which buttons are pressed. */
-    virtual uint32_t getButtonState() const = 0;
-
-    /* Sets the absolute location of the pointer. */
-    virtual void setPosition(float x, float y) = 0;
-
-    /* Gets the absolute location of the pointer. */
-    virtual void getPosition(float* outX, float* outY) const = 0;
-};
-
-} // namespace android
-
-#endif // _UI_POINTER_CONTROLLER_H
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 4ba8b5b..0d55f08 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -43,16 +43,12 @@
 LOCAL_SRC_FILES:= \
 	$(commonSources) \
 	EGLUtils.cpp \
-	EventHub.cpp \
 	EventRecurrence.cpp \
 	FramebufferNativeWindow.cpp \
 	GraphicBuffer.cpp \
 	GraphicBufferAllocator.cpp \
 	GraphicBufferMapper.cpp \
 	GraphicLog.cpp \
-	InputDispatcher.cpp \
-	InputManager.cpp \
-	InputReader.cpp \
 	InputTransport.cpp \
 	PixelFormat.cpp \
 	Rect.cpp \
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
index aa017b9..580d73c 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -7,8 +7,6 @@
 # Build the unit tests.
 test_src_files := \
     InputChannel_test.cpp \
-    InputReader_test.cpp \
-    InputDispatcher_test.cpp \
     InputPublisherAndConsumer_test.cpp
 
 shared_libraries := \
diff --git a/services/input/Android.mk b/services/input/Android.mk
new file mode 100644
index 0000000..37804ff
--- /dev/null
+++ b/services/input/Android.mk
@@ -0,0 +1,56 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    EventHub.cpp \
+    InputDispatcher.cpp \
+    InputManager.cpp \
+    InputReader.cpp \
+    PointerController.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libutils \
+    libhardware \
+    libhardware_legacy \
+    libsurfaceflinger_client \
+    libskia \
+    libui
+
+LOCAL_C_INCLUDES := \
+    external/skia/include/core
+
+LOCAL_MODULE:= libinput
+
+LOCAL_MODULE_TAGS := optional
+
+ifeq ($(TARGET_SIMULATOR),true)
+    LOCAL_LDLIBS += -lpthread
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/libs/ui/EventHub.cpp b/services/input/EventHub.cpp
similarity index 97%
rename from libs/ui/EventHub.cpp
rename to services/input/EventHub.cpp
index 4e9fad0..487e73f 100644
--- a/libs/ui/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -1,5 +1,19 @@
-//
-// Copyright 2005 The Android Open Source Project
+/*
+ * Copyright (C) 2005 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.
+ */
+
 //
 // Handle events, like key input and vsync.
 //
@@ -15,7 +29,8 @@
 
 //#define LOG_NDEBUG 0
 
-#include <ui/EventHub.h>
+#include "EventHub.h"
+
 #include <hardware_legacy/power.h>
 
 #include <cutils/properties.h>
@@ -58,18 +73,6 @@
 /* this macro computes the number of bytes needed to represent a bit array of the specified size */
 #define sizeof_bit_array(bits)  ((bits + 7) / 8)
 
-#ifndef ABS_MT_TOUCH_MAJOR
-#define ABS_MT_TOUCH_MAJOR      0x30    /* Major axis of touching ellipse */
-#endif
-
-#ifndef ABS_MT_POSITION_X
-#define ABS_MT_POSITION_X       0x35    /* Center X ellipse position */
-#endif
-
-#ifndef ABS_MT_POSITION_Y
-#define ABS_MT_POSITION_Y       0x36    /* Center Y ellipse position */
-#endif
-
 // Fd at index 0 is always reserved for inotify
 #define FIRST_ACTUAL_DEVICE_INDEX 1
 
diff --git a/include/ui/EventHub.h b/services/input/EventHub.h
similarity index 98%
rename from include/ui/EventHub.h
rename to services/input/EventHub.h
index 8f922a5..74b7ec5 100644
--- a/include/ui/EventHub.h
+++ b/services/input/EventHub.h
@@ -133,7 +133,7 @@
  * environment.  In addition, the event hub generates fake input events to indicate
  * when devices are added or removed.
  *
- * The event hub provies a stream of input events (via the getEvent function).
+ * The event hub provides a stream of input events (via the getEvent function).
  * It also supports querying the current actual state of input devices such as identifying
  * which keys are currently down.  Finally, the event hub keeps track of the capabilities of
  * individual input devices, such as their class and the set of key codes that they support.
diff --git a/libs/ui/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
similarity index 99%
rename from libs/ui/InputDispatcher.cpp
rename to services/input/InputDispatcher.cpp
index 0548e61..3675021 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -1,8 +1,19 @@
-//
-// Copyright 2010 The Android Open Source Project
-//
-// The input dispatcher.
-//
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 #define LOG_TAG "InputDispatcher"
 
 //#define LOG_NDEBUG 0
@@ -37,8 +48,9 @@
 // Log debug messages about the app switch latency optimization.
 #define DEBUG_APP_SWITCH 0
 
+#include "InputDispatcher.h"
+
 #include <cutils/log.h>
-#include <ui/InputDispatcher.h>
 #include <ui/PowerManager.h>
 
 #include <stddef.h>
diff --git a/include/ui/InputDispatcher.h b/services/input/InputDispatcher.h
similarity index 100%
rename from include/ui/InputDispatcher.h
rename to services/input/InputDispatcher.h
diff --git a/libs/ui/InputManager.cpp b/services/input/InputManager.cpp
similarity index 74%
rename from libs/ui/InputManager.cpp
rename to services/input/InputManager.cpp
index 09fce38..5dfa5d5 100644
--- a/libs/ui/InputManager.cpp
+++ b/services/input/InputManager.cpp
@@ -1,16 +1,26 @@
-//
-// Copyright 2010 The Android Open Source Project
-//
-// The input manager.
-//
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 #define LOG_TAG "InputManager"
 
 //#define LOG_NDEBUG 0
 
+#include "InputManager.h"
+
 #include <cutils/log.h>
-#include <ui/InputManager.h>
-#include <ui/InputReader.h>
-#include <ui/InputDispatcher.h>
 
 namespace android {
 
diff --git a/include/ui/InputManager.h b/services/input/InputManager.h
similarity index 92%
rename from include/ui/InputManager.h
rename to services/input/InputManager.h
index 568568b..df4d299e 100644
--- a/include/ui/InputManager.h
+++ b/services/input/InputManager.h
@@ -21,8 +21,12 @@
  * Native input manager.
  */
 
-#include <ui/EventHub.h>
+#include "EventHub.h"
+#include "InputReader.h"
+#include "InputDispatcher.h"
+
 #include <ui/Input.h>
+#include <ui/InputTransport.h>
 #include <utils/Errors.h>
 #include <utils/Vector.h>
 #include <utils/Timers.h>
@@ -31,16 +35,6 @@
 
 namespace android {
 
-class InputChannel;
-
-class InputReaderInterface;
-class InputReaderPolicyInterface;
-class InputReaderThread;
-
-class InputDispatcherInterface;
-class InputDispatcherPolicyInterface;
-class InputDispatcherThread;
-
 /*
  * The input manager is the core of the system event processing.
  *
diff --git a/libs/ui/InputReader.cpp b/services/input/InputReader.cpp
similarity index 98%
rename from libs/ui/InputReader.cpp
rename to services/input/InputReader.cpp
index a11a010..7a45de6 100644
--- a/libs/ui/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -1,8 +1,19 @@
-//
-// Copyright 2010 The Android Open Source Project
-//
-// The input reader.
-//
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 #define LOG_TAG "InputReader"
 
 //#define LOG_NDEBUG 0
@@ -22,8 +33,9 @@
 // Log debug messages about pointer assignment calculations.
 #define DEBUG_POINTER_ASSIGNMENT 0
 
+#include "InputReader.h"
+
 #include <cutils/log.h>
-#include <ui/InputReader.h>
 #include <ui/Keyboard.h>
 #include <ui/VirtualKeyMap.h>
 
@@ -84,7 +96,7 @@
         sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
 
 int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
-    if (orientation != InputReaderPolicyInterface::ROTATION_0) {
+    if (orientation != DISPLAY_ORIENTATION_0) {
         for (int i = 0; i < keyCodeRotationMapSize; i++) {
             if (keyCode == keyCodeRotationMap[i][0]) {
                 return keyCodeRotationMap[i][orientation];
@@ -860,7 +872,7 @@
                 int32_t orientation;
                 if (!getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
                         NULL, NULL, & orientation)) {
-                    orientation = InputReaderPolicyInterface::ROTATION_0;
+                    orientation = DISPLAY_ORIENTATION_0;
                 }
 
                 keyCode = rotateKeyCode(keyCode, orientation);
@@ -1195,23 +1207,23 @@
             int32_t orientation;
             if (! getPolicy()->getDisplayInfo(mParameters.associatedDisplayId,
                     NULL, NULL, & orientation)) {
-                orientation = InputReaderPolicyInterface::ROTATION_0;
+                orientation = DISPLAY_ORIENTATION_0;
             }
 
             float temp;
             switch (orientation) {
-            case InputReaderPolicyInterface::ROTATION_90:
+            case DISPLAY_ORIENTATION_90:
                 temp = deltaX;
                 deltaX = deltaY;
                 deltaY = -temp;
                 break;
 
-            case InputReaderPolicyInterface::ROTATION_180:
+            case DISPLAY_ORIENTATION_180:
                 deltaX = -deltaX;
                 deltaY = -deltaY;
                 break;
 
-            case InputReaderPolicyInterface::ROTATION_270:
+            case DISPLAY_ORIENTATION_270:
                 temp = deltaX;
                 deltaX = -deltaY;
                 deltaY = temp;
@@ -1485,7 +1497,7 @@
 
 bool TouchInputMapper::configureSurfaceLocked() {
     // Update orientation and dimensions if needed.
-    int32_t orientation = InputReaderPolicyInterface::ROTATION_0;
+    int32_t orientation = DISPLAY_ORIENTATION_0;
     int32_t width = mRawAxes.x.getRange();
     int32_t height = mRawAxes.y.getRange();
 
@@ -1677,8 +1689,8 @@
         // Compute oriented surface dimensions, precision, and scales.
         float orientedXScale, orientedYScale;
         switch (mLocked.surfaceOrientation) {
-        case InputReaderPolicyInterface::ROTATION_90:
-        case InputReaderPolicyInterface::ROTATION_270:
+        case DISPLAY_ORIENTATION_90:
+        case DISPLAY_ORIENTATION_270:
             mLocked.orientedSurfaceWidth = mLocked.surfaceHeight;
             mLocked.orientedSurfaceHeight = mLocked.surfaceWidth;
             mLocked.orientedXPrecision = mLocked.yPrecision;
@@ -2553,7 +2565,7 @@
 
             // Adjust coords for orientation.
             switch (mLocked.surfaceOrientation) {
-            case InputReaderPolicyInterface::ROTATION_90: {
+            case DISPLAY_ORIENTATION_90: {
                 float xTemp = x;
                 x = y;
                 y = mLocked.surfaceWidth - xTemp;
@@ -2563,13 +2575,13 @@
                 }
                 break;
             }
-            case InputReaderPolicyInterface::ROTATION_180: {
+            case DISPLAY_ORIENTATION_180: {
                 x = mLocked.surfaceWidth - x;
                 y = mLocked.surfaceHeight - y;
                 orientation = - orientation;
                 break;
             }
-            case InputReaderPolicyInterface::ROTATION_270: {
+            case DISPLAY_ORIENTATION_270: {
                 float xTemp = x;
                 x = mLocked.surfaceHeight - y;
                 y = xTemp;
diff --git a/include/ui/InputReader.h b/services/input/InputReader.h
similarity index 99%
rename from include/ui/InputReader.h
rename to services/input/InputReader.h
index 00a06be..8c849c4 100644
--- a/include/ui/InputReader.h
+++ b/services/input/InputReader.h
@@ -17,10 +17,12 @@
 #ifndef _UI_INPUT_READER_H
 #define _UI_INPUT_READER_H
 
-#include <ui/EventHub.h>
+#include "EventHub.h"
+#include "InputDispatcher.h"
+#include "PointerController.h"
+
 #include <ui/Input.h>
-#include <ui/InputDispatcher.h>
-#include <ui/PointerController.h>
+#include <ui/DisplayInfo.h>
 #include <utils/KeyedVector.h>
 #include <utils/threads.h>
 #include <utils/Timers.h>
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
new file mode 100644
index 0000000..ebc58ee
--- /dev/null
+++ b/services/input/PointerController.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PointerController"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about pointer updates
+#define DEBUG_POINTER_UPDATES 0
+
+#include "PointerController.h"
+
+#include <cutils/log.h>
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
+#include <SkXfermode.h>
+
+namespace android {
+
+// --- PointerController ---
+
+PointerController::PointerController(int32_t pointerLayer) :
+    mPointerLayer(pointerLayer) {
+    AutoMutex _l(mLock);
+
+    mLocked.displayWidth = -1;
+    mLocked.displayHeight = -1;
+    mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
+
+    mLocked.pointerX = 0;
+    mLocked.pointerY = 0;
+    mLocked.buttonState = 0;
+
+    mLocked.iconBitmap = NULL;
+    mLocked.iconHotSpotX = 0;
+    mLocked.iconHotSpotY = 0;
+
+    mLocked.wantVisible = false;
+    mLocked.visible = false;
+    mLocked.drawn = false;
+}
+
+PointerController::~PointerController() {
+    if (mSurfaceControl != NULL) {
+        mSurfaceControl->clear();
+        mSurfaceControl.clear();
+    }
+
+    if (mSurfaceComposerClient != NULL) {
+        mSurfaceComposerClient->dispose();
+        mSurfaceComposerClient.clear();
+    }
+
+    delete mLocked.iconBitmap;
+}
+
+bool PointerController::getBounds(float* outMinX, float* outMinY,
+        float* outMaxX, float* outMaxY) const {
+    AutoMutex _l(mLock);
+
+    return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
+}
+
+bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
+        float* outMaxX, float* outMaxY) const {
+    if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
+        return false;
+    }
+
+    *outMinX = 0;
+    *outMinY = 0;
+    switch (mLocked.displayOrientation) {
+    case DISPLAY_ORIENTATION_90:
+    case DISPLAY_ORIENTATION_270:
+        *outMaxX = mLocked.displayHeight;
+        *outMaxY = mLocked.displayWidth;
+        break;
+    default:
+        *outMaxX = mLocked.displayWidth;
+        *outMaxY = mLocked.displayHeight;
+        break;
+    }
+    return true;
+}
+
+void PointerController::move(float deltaX, float deltaY) {
+#if DEBUG_POINTER_UPDATES
+    LOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
+#endif
+    if (deltaX == 0.0f && deltaY == 0.0f) {
+        return;
+    }
+
+    AutoMutex _l(mLock);
+
+    setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
+}
+
+void PointerController::setButtonState(uint32_t buttonState) {
+#if DEBUG_POINTER_UPDATES
+    LOGD("Set button state 0x%08x", buttonState);
+#endif
+    AutoMutex _l(mLock);
+
+    if (mLocked.buttonState != buttonState) {
+        mLocked.buttonState = buttonState;
+        mLocked.wantVisible = true;
+        updateLocked();
+    }
+}
+
+uint32_t PointerController::getButtonState() const {
+    AutoMutex _l(mLock);
+
+    return mLocked.buttonState;
+}
+
+void PointerController::setPosition(float x, float y) {
+#if DEBUG_POINTER_UPDATES
+    LOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
+#endif
+    AutoMutex _l(mLock);
+
+    setPositionLocked(x, y);
+}
+
+void PointerController::setPositionLocked(float x, float y) {
+    float minX, minY, maxX, maxY;
+    if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
+        if (x <= minX) {
+            mLocked.pointerX = minX;
+        } else if (x >= maxX) {
+            mLocked.pointerX = maxX;
+        } else {
+            mLocked.pointerX = x;
+        }
+        if (y <= minY) {
+            mLocked.pointerY = minY;
+        } else if (y >= maxY) {
+            mLocked.pointerY = maxY;
+        } else {
+            mLocked.pointerY = y;
+        }
+        mLocked.wantVisible = true;
+        updateLocked();
+    }
+}
+
+void PointerController::getPosition(float* outX, float* outY) const {
+    AutoMutex _l(mLock);
+
+    *outX = mLocked.pointerX;
+    *outY = mLocked.pointerY;
+}
+
+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;
+        }
+
+        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.");
+    }
+}
+
+void PointerController::setDisplaySize(int32_t width, int32_t height) {
+    AutoMutex _l(mLock);
+
+    if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
+        mLocked.displayWidth = width;
+        mLocked.displayHeight = height;
+
+        float minX, minY, maxX, maxY;
+        if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
+            mLocked.pointerX = (minX + maxX) * 0.5f;
+            mLocked.pointerY = (minY + maxY) * 0.5f;
+        } else {
+            mLocked.pointerX = 0;
+            mLocked.pointerY = 0;
+        }
+
+        updateLocked();
+    }
+}
+
+void PointerController::setDisplayOrientation(int32_t orientation) {
+    AutoMutex _l(mLock);
+
+    if (mLocked.displayOrientation != orientation) {
+        float absoluteX, absoluteY;
+
+        // Map from oriented display coordinates to absolute display coordinates.
+        switch (mLocked.displayOrientation) {
+        case DISPLAY_ORIENTATION_90:
+            absoluteX = mLocked.displayWidth - mLocked.pointerY;
+            absoluteY = mLocked.pointerX;
+            break;
+        case DISPLAY_ORIENTATION_180:
+            absoluteX = mLocked.displayWidth - mLocked.pointerX;
+            absoluteY = mLocked.displayHeight - mLocked.pointerY;
+            break;
+        case DISPLAY_ORIENTATION_270:
+            absoluteX = mLocked.pointerY;
+            absoluteY = mLocked.displayHeight - mLocked.pointerX;
+            break;
+        default:
+            absoluteX = mLocked.pointerX;
+            absoluteY = mLocked.pointerY;
+            break;
+        }
+
+        // Map from absolute display coordinates to oriented display coordinates.
+        switch (orientation) {
+        case DISPLAY_ORIENTATION_90:
+            mLocked.pointerX = absoluteY;
+            mLocked.pointerY = mLocked.displayWidth - absoluteX;
+            break;
+        case DISPLAY_ORIENTATION_180:
+            mLocked.pointerX = mLocked.displayWidth - absoluteX;
+            mLocked.pointerY = mLocked.displayHeight - absoluteY;
+            break;
+        case DISPLAY_ORIENTATION_270:
+            mLocked.pointerX = mLocked.displayHeight - absoluteY;
+            mLocked.pointerY = absoluteX;
+            break;
+        default:
+            mLocked.pointerX = absoluteX;
+            mLocked.pointerY = absoluteY;
+            break;
+        }
+
+        mLocked.displayOrientation = orientation;
+
+        updateLocked();
+    }
+}
+
+void PointerController::setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) {
+    AutoMutex _l(mLock);
+
+    delete mLocked.iconBitmap;
+    mLocked.iconBitmap = bitmap ? new SkBitmap(*bitmap) : NULL;
+    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;
+}
+
+} // namespace android
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
new file mode 100644
index 0000000..a2a9955
--- /dev/null
+++ b/services/input/PointerController.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_POINTER_CONTROLLER_H
+#define _UI_POINTER_CONTROLLER_H
+
+#include <ui/DisplayInfo.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <SkBitmap.h>
+
+namespace android {
+
+enum {
+    POINTER_BUTTON_1 = 1 << 0,
+};
+
+/**
+ * Interface for tracking a single (mouse) pointer.
+ *
+ * The pointer controller is responsible for providing synchronization and for tracking
+ * display orientation changes if needed.
+ */
+class PointerControllerInterface : public virtual RefBase {
+protected:
+    PointerControllerInterface() { }
+    virtual ~PointerControllerInterface() { }
+
+public:
+    /* Gets the bounds of the region that the pointer can traverse.
+     * Returns true if the bounds are available. */
+    virtual bool getBounds(float* outMinX, float* outMinY,
+            float* outMaxX, float* outMaxY) const = 0;
+
+    /* Move the pointer. */
+    virtual void move(float deltaX, float deltaY) = 0;
+
+    /* Sets a mask that indicates which buttons are pressed. */
+    virtual void setButtonState(uint32_t buttonState) = 0;
+
+    /* Gets a mask that indicates which buttons are pressed. */
+    virtual uint32_t getButtonState() const = 0;
+
+    /* Sets the absolute location of the pointer. */
+    virtual void setPosition(float x, float y) = 0;
+
+    /* Gets the absolute location of the pointer. */
+    virtual void getPosition(float* outX, float* outY) const = 0;
+};
+
+
+/*
+ * Tracks pointer movements and draws the pointer sprite to a surface.
+ *
+ * Handles pointer acceleration and animation.
+ */
+class PointerController : public PointerControllerInterface {
+protected:
+    virtual ~PointerController();
+
+public:
+    PointerController(int32_t pointerLayer);
+
+    virtual bool getBounds(float* outMinX, float* outMinY,
+            float* outMaxX, float* outMaxY) const;
+    virtual void move(float deltaX, float deltaY);
+    virtual void setButtonState(uint32_t buttonState);
+    virtual uint32_t getButtonState() const;
+    virtual void setPosition(float x, float y);
+    virtual void getPosition(float* outX, float* outY) const;
+
+    void setDisplaySize(int32_t width, int32_t height);
+    void setDisplayOrientation(int32_t orientation);
+    void setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY);
+
+private:
+    mutable Mutex mLock;
+
+    int32_t mPointerLayer;
+    sp<SurfaceComposerClient> mSurfaceComposerClient;
+    sp<SurfaceControl> mSurfaceControl;
+
+    struct Locked {
+        int32_t displayWidth;
+        int32_t displayHeight;
+        int32_t displayOrientation;
+
+        float pointerX;
+        float pointerY;
+        uint32_t buttonState;
+
+        SkBitmap* iconBitmap;
+        float iconHotSpotX;
+        float iconHotSpotY;
+
+        bool wantVisible;
+        bool visible;
+        bool drawn;
+    } 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);
+};
+
+} // namespace android
+
+#endif // _UI_POINTER_CONTROLLER_H
diff --git a/services/input/tests/Android.mk b/services/input/tests/Android.mk
new file mode 100644
index 0000000..799eb76
--- /dev/null
+++ b/services/input/tests/Android.mk
@@ -0,0 +1,50 @@
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+# Build the unit tests.
+test_src_files := \
+    InputReader_test.cpp \
+    InputDispatcher_test.cpp
+
+shared_libraries := \
+    libcutils \
+    libutils \
+    libhardware \
+    libhardware_legacy \
+    libui \
+    libsurfaceflinger_client \
+    libskia \
+    libstlport \
+    libinput
+
+static_libraries := \
+    libgtest \
+    libgtest_main
+
+c_includes := \
+    bionic \
+    bionic/libstdc++/include \
+    external/gtest/include \
+    external/stlport/stlport \
+    external/skia/include/core
+
+module_tags := eng tests
+
+$(foreach file,$(test_src_files), \
+    $(eval include $(CLEAR_VARS)) \
+    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+    $(eval LOCAL_C_INCLUDES := $(c_includes)) \
+    $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+    $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
+    $(eval include $(BUILD_EXECUTABLE)) \
+)
+
+# Build the manual test programs.
+include $(call all-subdir-makefiles)
+
+endif
\ No newline at end of file
diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/services/input/tests/InputDispatcher_test.cpp
similarity index 99%
rename from libs/ui/tests/InputDispatcher_test.cpp
rename to services/input/tests/InputDispatcher_test.cpp
index 7e17c57..b79633a 100644
--- a/libs/ui/tests/InputDispatcher_test.cpp
+++ b/services/input/tests/InputDispatcher_test.cpp
@@ -2,7 +2,8 @@
 // Copyright 2010 The Android Open Source Project
 //
 
-#include <ui/InputDispatcher.h>
+#include "../InputDispatcher.h"
+
 #include <gtest/gtest.h>
 #include <linux/input.h>
 
diff --git a/libs/ui/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
similarity index 98%
rename from libs/ui/tests/InputReader_test.cpp
rename to services/input/tests/InputReader_test.cpp
index 50d3383..9d2c52f 100644
--- a/libs/ui/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -2,7 +2,8 @@
 // Copyright 2010 The Android Open Source Project
 //
 
-#include <ui/InputReader.h>
+#include "../InputReader.h"
+
 #include <utils/List.h>
 #include <gtest/gtest.h>
 #include <math.h>
@@ -40,7 +41,8 @@
     virtual ~FakePointerController() { }
 
 public:
-    FakePointerController() {
+    FakePointerController() :
+        mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0) {
     }
 
     void setBounds(float minX, float minY, float maxX, float maxY) {
@@ -54,8 +56,8 @@
 private:
     virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
         *outMinX = mMinX;
-        *outMaxX = mMinX;
         *outMinY = mMinY;
+        *outMaxX = mMaxX;
         *outMaxY = mMaxY;
         return mHaveBounds;
     }
@@ -973,6 +975,9 @@
 }
 
 TEST_F(InputReaderTest, GetInputConfiguration_WhenMousePresent_ReturnsNoNavigation) {
+    sp<FakePointerController> controller = new FakePointerController();
+    mFakePolicy->setPointerController(0, controller);
+
     PropertyMap configuration;
     configuration.addProperty(String8("cursor.mode"), String8("pointer"));
     ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("mouse"),
@@ -1044,6 +1049,9 @@
 }
 
 TEST_F(InputReaderTest, GetInputDeviceIds) {
+    sp<FakePointerController> controller = new FakePointerController();
+    mFakePolicy->setPointerController(2, controller);
+
     ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"),
             INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY, NULL));
     ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("mouse"),
@@ -1675,7 +1683,7 @@
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_90);
+            DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1694,7 +1702,7 @@
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_0);
+            DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1706,7 +1714,7 @@
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_90);
+            DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1718,7 +1726,7 @@
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_180);
+            DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1730,7 +1738,7 @@
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_270);
+            DISPLAY_ORIENTATION_270);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
             KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -1746,7 +1754,7 @@
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_270);
+            DISPLAY_ORIENTATION_270);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, AKEYCODE_DPAD_UP, 1, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
@@ -1755,7 +1763,7 @@
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_180);
+            DISPLAY_ORIENTATION_180);
     process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, AKEYCODE_DPAD_UP, 0, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
@@ -1941,7 +1949,7 @@
     ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_X));
     ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_Y));
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
-            0.0f, 1.0f, 0.0f, 1.0f));
+            0.0f, 1.0f, 0.0f, 0.0f));
 
     // When the bounds are set, then there should be a valid motion range.
     mFakePointerController->setBounds(1, 2, 800, 480);
@@ -1950,11 +1958,11 @@
     mapper->populateDeviceInfo(&info2);
 
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X,
-            1, 800, 0.0f, 1.0f));
+            1, 800, 0.0f, 0.0f));
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y,
-            2, 480, 0.0f, 1.0f));
+            2, 480, 0.0f, 0.0f));
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE,
-            0.0f, 1.0f, 0.0f, 1.0f));
+            0.0f, 1.0f, 0.0f, 0.0f));
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) {
@@ -1969,6 +1977,8 @@
             -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_Y,
             -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
+            0.0f, 1.0f, 0.0f, 0.0f));
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
@@ -2151,7 +2161,7 @@
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
             DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_90);
+            DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2169,8 +2179,7 @@
     addMapperAndConfigure(mapper);
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_0);
+            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  1,  0));
@@ -2181,8 +2190,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_90);
+            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0, -1));
@@ -2193,8 +2201,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1,  1));
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_180);
+            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1, -1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0, -1,  0));
@@ -2205,8 +2212,7 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
-            DISPLAY_WIDTH, DISPLAY_HEIGHT,
-            InputReaderPolicyInterface::ROTATION_270);
+            DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  0,  0,  1));
@@ -2386,7 +2392,7 @@
 
 TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -2413,7 +2419,7 @@
 
 TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -2440,7 +2446,7 @@
 
 TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -2456,7 +2462,7 @@
     // Note: Ideally we should send cancels but the implementation is more straightforward
     // with up and this will only happen if a device is forcibly removed.
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -2489,7 +2495,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Reset_WhenNothingIsPressed_NothingMuchHappens) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -2515,7 +2521,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -2564,7 +2570,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -2678,7 +2684,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -2746,7 +2752,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -2836,7 +2842,7 @@
     FakeInputDispatcher::NotifyMotionArgs args;
 
     // Rotation 90.
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_90);
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
 
@@ -2857,7 +2863,7 @@
     FakeInputDispatcher::NotifyMotionArgs args;
 
     // Rotation 0.
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
 
@@ -2870,7 +2876,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
 
     // Rotation 90.
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_90);
+    prepareDisplay(DISPLAY_ORIENTATION_90);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
 
@@ -2883,7 +2889,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
 
     // Rotation 180.
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_180);
+    prepareDisplay(DISPLAY_ORIENTATION_180);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
 
@@ -2896,7 +2902,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
 
     // Rotation 270.
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_270);
+    prepareDisplay(DISPLAY_ORIENTATION_270);
     processDown(mapper, toRawX(50), toRawY(75));
     processSync(mapper);
 
@@ -2911,7 +2917,7 @@
 
 TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | PRESSURE | TOOL);
     addMapperAndConfigure(mapper);
 
@@ -3043,7 +3049,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
     MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -3294,7 +3300,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
     MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID);
     prepareVirtualKeys();
     addMapperAndConfigure(mapper);
@@ -3454,7 +3460,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
     MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR);
     addMapperAndConfigure(mapper);
 
@@ -3499,7 +3505,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
     MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | MINOR);
     addConfigurationProperty("touch.touchSize.calibration", "geometric");
     addConfigurationProperty("touch.toolSize.calibration", "geometric");
@@ -3540,7 +3546,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinearCalibration) {
     MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
     addConfigurationProperty("touch.touchSize.calibration", "pressure");
     addConfigurationProperty("touch.toolSize.calibration", "linear");
@@ -3596,7 +3602,7 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_AreaCalibration) {
     MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-    prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
     addConfigurationProperty("touch.touchSize.calibration", "pressure");
     addConfigurationProperty("touch.toolSize.calibration", "area");
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index b5becb5..410b8c2 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -19,10 +19,17 @@
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Environment;
 import android.os.SystemProperties;
 import android.util.Slog;
@@ -358,7 +365,49 @@
             pw.println(dumpStr);
         }
     }
-    
+
+    private static final class PointerIcon {
+        public Bitmap bitmap;
+        public float hotSpotX;
+        public float hotSpotY;
+
+        public static PointerIcon load(Resources resources, int resourceId) {
+            PointerIcon icon = new PointerIcon();
+
+            XmlResourceParser parser = resources.getXml(resourceId);
+            final int bitmapRes;
+            try {
+                XmlUtils.beginDocument(parser, "pointer-icon");
+
+                TypedArray a = resources.obtainAttributes(
+                        parser, com.android.internal.R.styleable.PointerIcon);
+                bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
+                icon.hotSpotX = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
+                icon.hotSpotY = a.getFloat(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
+                a.recycle();
+            } catch (Exception ex) {
+                Slog.e(TAG, "Exception parsing pointer icon resource.", ex);
+                return null;
+            } finally {
+                parser.close();
+            }
+
+            if (bitmapRes == 0) {
+                Slog.e(TAG, "<pointer-icon> is missing bitmap attribute");
+                return null;
+            }
+
+            Drawable drawable = resources.getDrawable(bitmapRes);
+            if (!(drawable instanceof BitmapDrawable)) {
+                Slog.e(TAG, "<pointer-icon> bitmap attribute must refer to a bitmap drawable");
+                return null;
+            }
+
+            icon.bitmap = ((BitmapDrawable)drawable).getBitmap();
+            return icon;
+        }
+    }
+
     /*
      * Callbacks from native.
      */
@@ -480,9 +529,15 @@
         @SuppressWarnings("unused")
         public int getPointerLayer() {
             return mWindowManagerService.mPolicy.windowTypeToLayerLw(
-                    WindowManager.LayoutParams.TYPE_DRAG)
+                    WindowManager.LayoutParams.TYPE_POINTER)
                     * WindowManagerService.TYPE_LAYER_MULTIPLIER
                     + WindowManagerService.TYPE_LAYER_OFFSET;
         }
+
+        @SuppressWarnings("unused")
+        public PointerIcon getPointerIcon() {
+            return PointerIcon.load(mContext.getResources(),
+                    com.android.internal.R.drawable.pointericon_default);
+        }
     }
 }
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index 845869c..4ccea6e 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -14,7 +14,10 @@
     onload.cpp
 
 LOCAL_C_INCLUDES += \
-    $(JNI_H_INCLUDE)
+    $(JNI_H_INCLUDE) \
+    frameworks/base/services \
+    frameworks/base/core/jni \
+    external/skia/include/core
 
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
@@ -25,6 +28,8 @@
     libsystem_server \
     libutils \
     libui \
+    libinput \
+    libskia \
     libsurfaceflinger_client \
     libusbhost
 
@@ -43,4 +48,3 @@
 LOCAL_MODULE:= libandroid_servers
 
 include $(BUILD_SHARED_LIBRARY)
-    
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index c757ada..5ed63f0 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -24,27 +24,23 @@
 // Log debug messages about InputDispatcherPolicy
 #define DEBUG_INPUT_DISPATCHER_POLICY 0
 
-// Log debug messages about PointerController
-#define DEBUG_POINTER_CONTROLLER 1
-
 
 #include "JNIHelp.h"
 #include "jni.h"
 #include <limits.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <ui/InputReader.h>
-#include <ui/InputDispatcher.h>
-#include <ui/InputManager.h>
-#include <ui/InputTransport.h>
+
 #include <utils/Log.h>
 #include <utils/threads.h>
-#include <surfaceflinger/Surface.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
-#include <surfaceflinger/ISurfaceComposer.h>
 
-#include "../../core/jni/android_view_KeyEvent.h"
-#include "../../core/jni/android_view_MotionEvent.h"
-#include "../../core/jni/android_view_InputChannel.h"
+#include <input/InputManager.h>
+#include <input/PointerController.h>
+
+#include <android_view_KeyEvent.h>
+#include <android_view_MotionEvent.h>
+#include <android_view_InputChannel.h>
+#include <android/graphics/GraphicsJNI.h>
+
 #include "com_android_server_PowerManagerService.h"
 
 namespace android {
@@ -67,6 +63,7 @@
     jmethodID getExcludedDeviceNames;
     jmethodID getMaxEventsPerSecond;
     jmethodID getPointerLayer;
+    jmethodID getPointerIcon;
 } gCallbacksClassInfo;
 
 static struct {
@@ -136,57 +133,13 @@
     jfieldID navigation;
 } gConfigurationClassInfo;
 
-// ----------------------------------------------------------------------------
+static struct {
+    jclass clazz;
 
-static inline nsecs_t now() {
-    return systemTime(SYSTEM_TIME_MONOTONIC);
-}
-
-// ----------------------------------------------------------------------------
-
-class PointerController : public PointerControllerInterface {
-protected:
-    virtual ~PointerController();
-
-public:
-    PointerController(int32_t pointerLayer);
-
-    virtual bool getBounds(float* outMinX, float* outMinY,
-            float* outMaxX, float* outMaxY) const;
-    virtual void move(float deltaX, float deltaY);
-    virtual void setButtonState(uint32_t buttonState);
-    virtual uint32_t getButtonState() const;
-    virtual void setPosition(float x, float y);
-    virtual void getPosition(float* outX, float* outY) const;
-
-    void setDisplaySize(int32_t width, int32_t height);
-    void setDisplayOrientation(int32_t orientation);
-
-private:
-    mutable Mutex mLock;
-
-    int32_t mPointerLayer;
-    sp<SurfaceComposerClient> mSurfaceComposerClient;
-    sp<SurfaceControl> mSurfaceControl;
-
-    struct Locked {
-        int32_t displayWidth;
-        int32_t displayHeight;
-        int32_t displayOrientation;
-
-        float pointerX;
-        float pointerY;
-        uint32_t buttonState;
-
-        bool wantVisible;
-        bool visible;
-        bool drawn;
-    } mLocked;
-
-    bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
-    void setPositionLocked(float x, float y);
-    void updateLocked();
-};
+    jfieldID bitmap;
+    jfieldID hotSpotX;
+    jfieldID hotSpotY;
+} gPointerIconClassInfo;
 
 // ----------------------------------------------------------------------------
 
@@ -538,13 +491,30 @@
     if (controller == NULL) {
         JNIEnv* env = jniEnv();
         jint layer = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getPointerLayer);
-        checkAndClearExceptionFromCallback(env, "getPointerLayer");
+        if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
+            layer = -1;
+        }
 
         controller = new PointerController(layer);
         mLocked.pointerController = controller;
 
         controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight);
         controller->setDisplayOrientation(mLocked.displayOrientation);
+
+        jobject iconObj = env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getPointerIcon);
+        if (!checkAndClearExceptionFromCallback(env, "getPointerIcon") && iconObj) {
+            jfloat iconHotSpotX = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotX);
+            jfloat iconHotSpotY = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotY);
+            jobject iconBitmapObj = env->GetObjectField(iconObj, gPointerIconClassInfo.bitmap);
+            if (iconBitmapObj) {
+                SkBitmap* iconBitmap = GraphicsJNI::getNativeBitmap(env, iconBitmapObj);
+                if (iconBitmap) {
+                    controller->setPointerIcon(iconBitmap, iconHotSpotX, iconHotSpotY);
+                }
+                env->DeleteLocalRef(iconBitmapObj);
+            }
+            env->DeleteLocalRef(iconObj);
+        }
     }
     return controller;
 }
@@ -984,293 +954,6 @@
     return result;
 }
 
-// --- PointerController ---
-
-PointerController::PointerController(int32_t pointerLayer) :
-    mPointerLayer(pointerLayer) {
-    AutoMutex _l(mLock);
-
-    mLocked.displayWidth = -1;
-    mLocked.displayHeight = -1;
-    mLocked.displayOrientation = InputReaderPolicyInterface::ROTATION_0;
-
-    mLocked.pointerX = 0;
-    mLocked.pointerY = 0;
-    mLocked.buttonState = 0;
-
-    mLocked.wantVisible = false;
-    mLocked.visible = false;
-    mLocked.drawn = false;
-}
-
-PointerController::~PointerController() {
-    mSurfaceControl.clear();
-    mSurfaceComposerClient.clear();
-}
-
-bool PointerController::getBounds(float* outMinX, float* outMinY,
-        float* outMaxX, float* outMaxY) const {
-    AutoMutex _l(mLock);
-
-    return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
-}
-
-bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
-        float* outMaxX, float* outMaxY) const {
-    if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
-        return false;
-    }
-
-    *outMinX = 0;
-    *outMinY = 0;
-    switch (mLocked.displayOrientation) {
-    case InputReaderPolicyInterface::ROTATION_90:
-    case InputReaderPolicyInterface::ROTATION_270:
-        *outMaxX = mLocked.displayHeight;
-        *outMaxY = mLocked.displayWidth;
-        break;
-    default:
-        *outMaxX = mLocked.displayWidth;
-        *outMaxY = mLocked.displayHeight;
-        break;
-    }
-    return true;
-}
-
-void PointerController::move(float deltaX, float deltaY) {
-#if DEBUG_POINTER_CONTROLLER
-    LOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
-#endif
-    if (deltaX == 0.0f && deltaY == 0.0f) {
-        return;
-    }
-
-    AutoMutex _l(mLock);
-
-    setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
-}
-
-void PointerController::setButtonState(uint32_t buttonState) {
-    AutoMutex _l(mLock);
-
-    if (mLocked.buttonState != buttonState) {
-        mLocked.buttonState = buttonState;
-        mLocked.wantVisible = true;
-        updateLocked();
-    }
-}
-
-uint32_t PointerController::getButtonState() const {
-    AutoMutex _l(mLock);
-
-    return mLocked.buttonState;
-}
-
-void PointerController::setPosition(float x, float y) {
-    AutoMutex _l(mLock);
-
-    setPositionLocked(x, y);
-}
-
-void PointerController::setPositionLocked(float x, float y) {
-    float minX, minY, maxX, maxY;
-    if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
-        if (x <= minX) {
-            mLocked.pointerX = minX;
-        } else if (x >= maxX) {
-            mLocked.pointerX = maxX;
-        } else {
-            mLocked.pointerX = x;
-        }
-        if (y <= minY) {
-            mLocked.pointerY = minY;
-        } else if (y >= maxY) {
-            mLocked.pointerY = maxY;
-        } else {
-            mLocked.pointerY = y;
-        }
-        mLocked.wantVisible = true;
-        updateLocked();
-    }
-}
-
-void PointerController::getPosition(float* outX, float* outY) const {
-    AutoMutex _l(mLock);
-
-    *outX = mLocked.pointerX;
-    *outY = mLocked.pointerY;
-}
-
-void PointerController::updateLocked() {
-#if DEBUG_POINTER_CONTROLLER
-    LOGD("Pointer at (%f, %f).", mLocked.pointerX, mLocked.pointerY);
-#endif
-
-    if (!mLocked.wantVisible && !mLocked.visible) {
-        return;
-    }
-
-    if (mSurfaceComposerClient == NULL) {
-        mSurfaceComposerClient = new SurfaceComposerClient();
-    }
-
-    if (mSurfaceControl == NULL) {
-        mSurfaceControl = mSurfaceComposerClient->createSurface(getpid(),
-                String8("Pointer"), 0, 16, 16, PIXEL_FORMAT_RGBA_8888);
-        if (mSurfaceControl == NULL) {
-            LOGE("Error creating pointer surface.");
-            return;
-        }
-    }
-
-    status_t status = mSurfaceComposerClient->openTransaction();
-    if (status) {
-        LOGE("Error opening surface transaction to update pointer surface.");
-        return;
-    }
-
-    if (mLocked.wantVisible) {
-        if (!mLocked.drawn) {
-            mLocked.drawn = true;
-
-            sp<Surface> surface = mSurfaceControl->getSurface();
-            Surface::SurfaceInfo surfaceInfo;
-            status = surface->lock(&surfaceInfo);
-            if (status) {
-                LOGE("Error %d locking pointer surface before drawing.", status);
-                goto CloseTransaction;
-            }
-
-            // TODO: Load pointers from assets and allow them to be set.
-            char* bitmap = (char*)surfaceInfo.bits;
-            ssize_t bpr = surfaceInfo.s * 4;
-            for (int y = 0; y < surfaceInfo.h; y++) {
-                for (int x = 0; x < surfaceInfo.w; x++) {
-                    bitmap[y * bpr + x * 4] = 128;
-                    bitmap[y * bpr + x * 4 + 1] = 255;
-                    bitmap[y * bpr + x * 4 + 2] = 128;
-                    bitmap[y * bpr + x * 4 + 3] = 255;
-                }
-            }
-
-            status = surface->unlockAndPost();
-            if (status) {
-                LOGE("Error %d unlocking pointer surface after drawing.", status);
-                goto CloseTransaction;
-            }
-        }
-
-        status = mSurfaceControl->setPosition(mLocked.pointerX, mLocked.pointerY);
-        if (status) {
-            LOGE("Error %d moving pointer surface.", status);
-            goto CloseTransaction;
-        }
-
-        if (!mLocked.visible) {
-            mLocked.visible = true;
-
-            mSurfaceControl->setLayer(mPointerLayer);
-
-            LOGD("XXX Show");
-            status = mSurfaceControl->show(mPointerLayer);
-            if (status) {
-                LOGE("Error %d showing pointer surface.", status);
-                goto CloseTransaction;
-            }
-        }
-    } else {
-        if (mLocked.visible) {
-            mLocked.visible = false;
-
-            if (mSurfaceControl != NULL) {
-                status = mSurfaceControl->hide();
-                if (status) {
-                    LOGE("Error %d hiding pointer surface.", status);
-                    goto CloseTransaction;
-                }
-            }
-        }
-    }
-
-CloseTransaction:
-    status = mSurfaceComposerClient->closeTransaction();
-    if (status) {
-        LOGE("Error closing surface transaction to update pointer surface.");
-    }
-}
-
-void PointerController::setDisplaySize(int32_t width, int32_t height) {
-    AutoMutex _l(mLock);
-
-    if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
-        mLocked.displayWidth = width;
-        mLocked.displayHeight = height;
-
-        float minX, minY, maxX, maxY;
-        if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
-            mLocked.pointerX = (minX + maxX) * 0.5f;
-            mLocked.pointerY = (minY + maxY) * 0.5f;
-        } else {
-            mLocked.pointerX = 0;
-            mLocked.pointerY = 0;
-        }
-
-        updateLocked();
-    }
-}
-
-void PointerController::setDisplayOrientation(int32_t orientation) {
-    AutoMutex _l(mLock);
-
-    if (mLocked.displayOrientation != orientation) {
-        float absoluteX, absoluteY;
-
-        // Map from oriented display coordinates to absolute display coordinates.
-        switch (mLocked.displayOrientation) {
-        case InputReaderPolicyInterface::ROTATION_90:
-            absoluteX = mLocked.displayWidth - mLocked.pointerY;
-            absoluteY = mLocked.pointerX;
-            break;
-        case InputReaderPolicyInterface::ROTATION_180:
-            absoluteX = mLocked.displayWidth - mLocked.pointerX;
-            absoluteY = mLocked.displayHeight - mLocked.pointerY;
-            break;
-        case InputReaderPolicyInterface::ROTATION_270:
-            absoluteX = mLocked.pointerY;
-            absoluteY = mLocked.displayHeight - mLocked.pointerX;
-            break;
-        default:
-            absoluteX = mLocked.pointerX;
-            absoluteY = mLocked.pointerY;
-            break;
-        }
-
-        // Map from absolute display coordinates to oriented display coordinates.
-        switch (orientation) {
-        case InputReaderPolicyInterface::ROTATION_90:
-            mLocked.pointerX = absoluteY;
-            mLocked.pointerY = mLocked.displayWidth - absoluteX;
-            break;
-        case InputReaderPolicyInterface::ROTATION_180:
-            mLocked.pointerX = mLocked.displayWidth - absoluteX;
-            mLocked.pointerY = mLocked.displayHeight - absoluteY;
-            break;
-        case InputReaderPolicyInterface::ROTATION_270:
-            mLocked.pointerX = mLocked.displayHeight - absoluteY;
-            mLocked.pointerY = absoluteX;
-            break;
-        default:
-            mLocked.pointerX = absoluteX;
-            mLocked.pointerY = absoluteY;
-            break;
-        }
-
-        mLocked.displayOrientation = orientation;
-
-        updateLocked();
-    }
-}
-
 
 // ----------------------------------------------------------------------------
 
@@ -1715,6 +1398,9 @@
     GET_METHOD_ID(gCallbacksClassInfo.getPointerLayer, gCallbacksClassInfo.clazz,
             "getPointerLayer", "()I");
 
+    GET_METHOD_ID(gCallbacksClassInfo.getPointerIcon, gCallbacksClassInfo.clazz,
+            "getPointerIcon", "()Lcom/android/server/InputManager$PointerIcon;");
+
     // InputWindow
 
     FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow");
@@ -1854,6 +1540,19 @@
     GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz,
             "navigation", "I");
 
+    // PointerIcon
+
+    FIND_CLASS(gPointerIconClassInfo.clazz, "com/android/server/InputManager$PointerIcon");
+
+    GET_FIELD_ID(gPointerIconClassInfo.bitmap, gPointerIconClassInfo.clazz,
+            "bitmap", "Landroid/graphics/Bitmap;");
+
+    GET_FIELD_ID(gPointerIconClassInfo.hotSpotX, gPointerIconClassInfo.clazz,
+            "hotSpotX", "F");
+
+    GET_FIELD_ID(gPointerIconClassInfo.hotSpotY, gPointerIconClassInfo.clazz,
+            "hotSpotY", "F");
+
     return 0;
 }