Merge "Make public pointer icon API with custom icons."
diff --git a/api/current.txt b/api/current.txt
index f5ccf0e..df6c473a3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -40337,6 +40337,38 @@
     field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
   }
 
+  public final class PointerIcon implements android.os.Parcelable {
+    method public static android.view.PointerIcon createCustomIcon(android.graphics.Bitmap, float, float);
+    method public int describeContents();
+    method public static android.view.PointerIcon getSystemIcon(android.content.Context, int);
+    method public static android.view.PointerIcon loadCustomIcon(android.content.res.Resources, int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR;
+    field public static final int STYLE_ALIAS = 1010; // 0x3f2
+    field public static final int STYLE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int STYLE_ARROW = 1000; // 0x3e8
+    field public static final int STYLE_CELL = 1006; // 0x3ee
+    field public static final int STYLE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int STYLE_COPY = 1011; // 0x3f3
+    field public static final int STYLE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int STYLE_DEFAULT = 1000; // 0x3e8
+    field public static final int STYLE_GRAB = 1020; // 0x3fc
+    field public static final int STYLE_GRABBING = 1021; // 0x3fd
+    field public static final int STYLE_HAND = 1002; // 0x3ea
+    field public static final int STYLE_HELP = 1003; // 0x3eb
+    field public static final int STYLE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int STYLE_NO_DROP = 1012; // 0x3f4
+    field public static final int STYLE_NULL = 0; // 0x0
+    field public static final int STYLE_TEXT = 1008; // 0x3f0
+    field public static final int STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int STYLE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int STYLE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int STYLE_WAIT = 1004; // 0x3ec
+    field public static final int STYLE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int STYLE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
   public class ScaleGestureDetector {
     ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
     ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -40696,6 +40728,7 @@
     method public android.view.ViewParent getParentForAccessibility();
     method public float getPivotX();
     method public float getPivotY();
+    method public android.view.PointerIcon getPointerIcon(android.view.MotionEvent, float, float);
     method public android.content.res.Resources getResources();
     method public final int getRight();
     method protected float getRightFadingEdgeStrength();
@@ -40978,6 +41011,7 @@
     method public void setPaddingRelative(int, int, int, int);
     method public void setPivotX(float);
     method public void setPivotY(float);
+    method public void setPointerIcon(android.view.PointerIcon);
     method public void setPressed(boolean);
     method public final void setRight(int);
     method public void setRotation(float);
diff --git a/api/system-current.txt b/api/system-current.txt
index bc63b48..ee975d1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -42675,6 +42675,38 @@
     field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
   }
 
+  public final class PointerIcon implements android.os.Parcelable {
+    method public static android.view.PointerIcon createCustomIcon(android.graphics.Bitmap, float, float);
+    method public int describeContents();
+    method public static android.view.PointerIcon getSystemIcon(android.content.Context, int);
+    method public static android.view.PointerIcon loadCustomIcon(android.content.res.Resources, int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR;
+    field public static final int STYLE_ALIAS = 1010; // 0x3f2
+    field public static final int STYLE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int STYLE_ARROW = 1000; // 0x3e8
+    field public static final int STYLE_CELL = 1006; // 0x3ee
+    field public static final int STYLE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int STYLE_COPY = 1011; // 0x3f3
+    field public static final int STYLE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int STYLE_DEFAULT = 1000; // 0x3e8
+    field public static final int STYLE_GRAB = 1020; // 0x3fc
+    field public static final int STYLE_GRABBING = 1021; // 0x3fd
+    field public static final int STYLE_HAND = 1002; // 0x3ea
+    field public static final int STYLE_HELP = 1003; // 0x3eb
+    field public static final int STYLE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int STYLE_NO_DROP = 1012; // 0x3f4
+    field public static final int STYLE_NULL = 0; // 0x0
+    field public static final int STYLE_TEXT = 1008; // 0x3f0
+    field public static final int STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int STYLE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int STYLE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int STYLE_WAIT = 1004; // 0x3ec
+    field public static final int STYLE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int STYLE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
   public class ScaleGestureDetector {
     ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
     ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -43034,6 +43066,7 @@
     method public android.view.ViewParent getParentForAccessibility();
     method public float getPivotX();
     method public float getPivotY();
+    method public android.view.PointerIcon getPointerIcon(android.view.MotionEvent, float, float);
     method public android.content.res.Resources getResources();
     method public final int getRight();
     method protected float getRightFadingEdgeStrength();
@@ -43316,6 +43349,7 @@
     method public void setPaddingRelative(int, int, int, int);
     method public void setPivotX(float);
     method public void setPivotY(float);
+    method public void setPointerIcon(android.view.PointerIcon);
     method public void setPressed(boolean);
     method public final void setRight(int);
     method public void setRotation(float);
diff --git a/api/test-current.txt b/api/test-current.txt
index a222037..c494895 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -40339,6 +40339,38 @@
     field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
   }
 
+  public final class PointerIcon implements android.os.Parcelable {
+    method public static android.view.PointerIcon createCustomIcon(android.graphics.Bitmap, float, float);
+    method public int describeContents();
+    method public static android.view.PointerIcon getSystemIcon(android.content.Context, int);
+    method public static android.view.PointerIcon loadCustomIcon(android.content.res.Resources, int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR;
+    field public static final int STYLE_ALIAS = 1010; // 0x3f2
+    field public static final int STYLE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int STYLE_ARROW = 1000; // 0x3e8
+    field public static final int STYLE_CELL = 1006; // 0x3ee
+    field public static final int STYLE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int STYLE_COPY = 1011; // 0x3f3
+    field public static final int STYLE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int STYLE_DEFAULT = 1000; // 0x3e8
+    field public static final int STYLE_GRAB = 1020; // 0x3fc
+    field public static final int STYLE_GRABBING = 1021; // 0x3fd
+    field public static final int STYLE_HAND = 1002; // 0x3ea
+    field public static final int STYLE_HELP = 1003; // 0x3eb
+    field public static final int STYLE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int STYLE_NO_DROP = 1012; // 0x3f4
+    field public static final int STYLE_NULL = 0; // 0x0
+    field public static final int STYLE_TEXT = 1008; // 0x3f0
+    field public static final int STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int STYLE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int STYLE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int STYLE_WAIT = 1004; // 0x3ec
+    field public static final int STYLE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int STYLE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
   public class ScaleGestureDetector {
     ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
     ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -40698,6 +40730,7 @@
     method public android.view.ViewParent getParentForAccessibility();
     method public float getPivotX();
     method public float getPivotY();
+    method public android.view.PointerIcon getPointerIcon(android.view.MotionEvent, float, float);
     method public android.content.res.Resources getResources();
     method public final int getRight();
     method protected float getRightFadingEdgeStrength();
@@ -40980,6 +41013,7 @@
     method public void setPaddingRelative(int, int, int, int);
     method public void setPivotX(float);
     method public void setPivotY(float);
+    method public void setPointerIcon(android.view.PointerIcon);
     method public void setPressed(boolean);
     method public final void setRight(int);
     method public void setRotation(float);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 174291e..ff33bd9 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -24,6 +24,7 @@
 import android.os.IBinder;
 import android.view.InputDevice;
 import android.view.InputEvent;
+import android.view.PointerIcon;
 
 /** @hide */
 interface IInputManager {
@@ -71,4 +72,5 @@
     void cancelVibrate(int deviceId, IBinder token);
 
     void setPointerIconShape(int shapeId);
+    void setCustomPointerIcon(in PointerIcon icon);
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 201afee..fab4718 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,6 +16,7 @@
 
 package android.hardware.input;
 
+import android.view.PointerIcon;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 
@@ -819,6 +820,15 @@
         }
     }
 
+    /** @hide */
+    public void setCustomPointerIcon(PointerIcon icon) {
+        try {
+            mIm.setCustomPointerIcon(icon);
+        } catch (RemoteException ex) {
+            // Do nothing.
+        }
+    }
+
     private void populateInputDevicesLocked() {
         if (mInputDevicesChangedListener == null) {
             final InputDevicesChangedListener listener = new InputDevicesChangedListener();
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 46b9a46..88cd7ca 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -784,6 +784,15 @@
     }
 
     /**
+     * Specifies the current custom pointer.
+     * @param icon the icon data.
+     * @hide
+     */
+    public void setCustomPointerIcon(PointerIcon icon) {
+        InputManager.getInstance().setCustomPointerIcon(icon);
+    }
+
+    /**
      * Provides information about the range of values for a particular {@link MotionEvent} axis.
      *
      * @see InputDevice#getMotionRange(int)
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index d2a7d4a..aa879cd 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -16,8 +16,10 @@
 
 package android.view;
 
+import android.annotation.NonNull;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.SparseArray;
 import com.android.internal.util.XmlUtils;
 
 import android.annotation.XmlRes;
@@ -39,13 +41,11 @@
  * Pointer icons can be provided either by the system using system styles,
  * or by applications using bitmaps or application resources.
  * </p>
- *
- * @hide
  */
 public final class PointerIcon implements Parcelable {
     private static final String TAG = "PointerIcon";
 
-    /** Style constant: Custom icon with a user-supplied bitmap. */
+    /** {@hide} Style constant: Custom icon with a user-supplied bitmap. */
     public static final int STYLE_CUSTOM = -1;
 
     /** Style constant: Null icon.  It has no bitmap. */
@@ -54,6 +54,7 @@
     /** Style constant: no icons are specified. If all views uses this, then falls back
      * to the default style, but this is helpful to distinguish a view explicitly want
      * to have the default icon.
+     * @hide
      */
     public static final int STYLE_NOT_SPECIFIED = 1;
 
@@ -135,10 +136,11 @@
     // conflicts with any system styles that may be defined in the future.
     private static final int STYLE_OEM_FIRST = 10000;
 
-    /** {@hide} The default pointer icon. */
+    /** The default pointer icon. */
     public static final int STYLE_DEFAULT = STYLE_ARROW;
 
     private static final PointerIcon gNullIcon = new PointerIcon(STYLE_NULL);
+    private static final SparseArray<PointerIcon> gSystemIcons = new SparseArray<PointerIcon>();
 
     private final int mStyle;
     private int mSystemIconResourceId;
@@ -160,6 +162,7 @@
      * @return The null pointer icon.
      *
      * @see #STYLE_NULL
+     * @hide
      */
     public static PointerIcon getNullIcon() {
         return gNullIcon;
@@ -172,8 +175,9 @@
      * @return The default pointer icon.
      *
      * @throws IllegalArgumentException if context is null.
+     * @hide
      */
-    public static PointerIcon getDefaultIcon(Context context) {
+    public static PointerIcon getDefaultIcon(@NonNull Context context) {
         return getSystemIcon(context, STYLE_DEFAULT);
     }
 
@@ -187,7 +191,7 @@
      *
      * @throws IllegalArgumentException if context is null.
      */
-    public static PointerIcon getSystemIcon(Context context, int style) {
+    public static PointerIcon getSystemIcon(@NonNull Context context, int style) {
         if (context == null) {
             throw new IllegalArgumentException("context must not be null");
         }
@@ -196,6 +200,11 @@
             return gNullIcon;
         }
 
+        PointerIcon icon = gSystemIcons.get(style);
+        if (icon != null) {
+            return icon;
+        }
+
         int styleIndex = getSystemIconStyleIndex(style);
         if (styleIndex == 0) {
             styleIndex = getSystemIconStyleIndex(STYLE_DEFAULT);
@@ -217,12 +226,13 @@
             return style == STYLE_DEFAULT ? gNullIcon : getSystemIcon(context, STYLE_DEFAULT);
         }
 
-        PointerIcon icon = new PointerIcon(style);
+        icon = new PointerIcon(style);
         if ((resourceId & 0xff000000) == 0x01000000) {
             icon.mSystemIconResourceId = resourceId;
         } else {
             icon.loadResource(context, context.getResources(), resourceId);
         }
+        gSystemIcons.append(style, icon);
         return icon;
     }
 
@@ -239,7 +249,8 @@
      * @throws IllegalArgumentException if bitmap is null, or if the x/y hotspot
      *         parameters are invalid.
      */
-    public static PointerIcon createCustomIcon(Bitmap bitmap, float hotSpotX, float hotSpotY) {
+    public static PointerIcon createCustomIcon(
+            @NonNull Bitmap bitmap, float hotSpotX, float hotSpotY) {
         if (bitmap == null) {
             throw new IllegalArgumentException("bitmap must not be null");
         }
@@ -273,7 +284,7 @@
      * @throws Resources.NotFoundException if the resource was not found or the drawable
      * linked in the resource was not found.
      */
-    public static PointerIcon loadCustomIcon(Resources resources, @XmlRes int resourceId) {
+    public static PointerIcon loadCustomIcon(@NonNull Resources resources, @XmlRes int resourceId) {
         if (resources == null) {
             throw new IllegalArgumentException("resources must not be null");
         }
@@ -291,10 +302,9 @@
      * @return The loaded pointer icon.
      *
      * @throws IllegalArgumentException if context is null.
-     * @see #isLoaded()
      * @hide
      */
-    public PointerIcon load(Context context) {
+    public PointerIcon load(@NonNull Context context) {
         if (context == null) {
             throw new IllegalArgumentException("context must not be null");
         }
@@ -309,83 +319,11 @@
         return result;
     }
 
-    /**
-     * Returns true if the pointer icon style is {@link #STYLE_NULL}.
-     *
-     * @return True if the pointer icon style is {@link #STYLE_NULL}.
-     */
-    public boolean isNullIcon() {
-        return mStyle == STYLE_NULL;
-    }
-
-    /**
-     * Returns true if the pointer icon has been loaded and its bitmap and hotspot
-     * information are available.
-     *
-     * @return True if the pointer icon is loaded.
-     * @see #load(Context)
-     */
-    public boolean isLoaded() {
-        return mBitmap != null || mStyle == STYLE_NULL;
-    }
-
-    /**
-     * Gets the style of the pointer icon.
-     *
-     * @return The pointer icon style.
-     */
+    /** @hide */
     public int getStyle() {
         return mStyle;
     }
 
-    /**
-     * Gets the bitmap of the pointer icon.
-     *
-     * @return The pointer icon bitmap, or null if the style is {@link #STYLE_NULL}.
-     *
-     * @throws IllegalStateException if the bitmap is not loaded.
-     * @see #isLoaded()
-     * @see #load(Context)
-     */
-    public Bitmap getBitmap() {
-        throwIfIconIsNotLoaded();
-        return mBitmap;
-    }
-
-    /**
-     * Gets the X offset of the pointer icon hotspot.
-     *
-     * @return The hotspot X offset.
-     *
-     * @throws IllegalStateException if the bitmap is not loaded.
-     * @see #isLoaded()
-     * @see #load(Context)
-     */
-    public float getHotSpotX() {
-        throwIfIconIsNotLoaded();
-        return mHotSpotX;
-    }
-
-    /**
-     * Gets the Y offset of the pointer icon hotspot.
-     *
-     * @return The hotspot Y offset.
-     *
-     * @throws IllegalStateException if the bitmap is not loaded.
-     * @see #isLoaded()
-     * @see #load(Context)
-     */
-    public float getHotSpotY() {
-        throwIfIconIsNotLoaded();
-        return mHotSpotY;
-    }
-
-    private void throwIfIconIsNotLoaded() {
-        if (!isLoaded()) {
-            throw new IllegalStateException("The icon is not loaded.");
-        }
-    }
-
     public static final Parcelable.Creator<PointerIcon> CREATOR
             = new Parcelable.Creator<PointerIcon>() {
         public PointerIcon createFromParcel(Parcel in) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3db18066..2368642 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2628,7 +2628,7 @@
     static final int PFLAG3_POINTER_ICON_LSHIFT = 15;
 
     /**
-     * Value indicating {@link PointerIcon.STYLE_NOT_SPECIFIED}.
+     * Value indicating no specific pointer icons.
      */
     private static final int PFLAG3_POINTER_ICON_NOT_SPECIFIED = 0 << PFLAG3_POINTER_ICON_LSHIFT;
 
@@ -2638,14 +2638,9 @@
     private static final int PFLAG3_POINTER_ICON_NULL = 1 << PFLAG3_POINTER_ICON_LSHIFT;
 
     /**
-     * Value incicating {@link PointerIcon.STYLE_CUSTOM}.
-     */
-    private static final int PFLAG3_POINTER_ICON_CUSTOM = 2 << PFLAG3_POINTER_ICON_LSHIFT;
-
-    /**
      * The base value for other pointer icon shapes.
      */
-    private static final int PFLAG3_POINTER_ICON_VALUE_START = 3 << PFLAG3_POINTER_ICON_LSHIFT;
+    private static final int PFLAG3_POINTER_ICON_VALUE_START = 2 << PFLAG3_POINTER_ICON_LSHIFT;
 
     /**
      * Always allow a user to over-scroll this view, provided it is a
@@ -3927,6 +3922,11 @@
     private HandlerActionQueue mRunQueue;
 
     /**
+     * The pointer icon when the mouse hovers on this view. The default is null.
+     */
+    private PointerIcon mPointerIcon;
+
+    /**
      * @hide
      */
     String mStartActivityRequestWho;
@@ -4490,7 +4490,7 @@
                 case R.styleable.View_pointerShape:
                     final int pointerShape = a.getInt(attr, PointerIcon.STYLE_NOT_SPECIFIED);
                     if (pointerShape != PointerIcon.STYLE_NOT_SPECIFIED) {
-                        setPointerShape(pointerShape);
+                        setPointerIcon(PointerIcon.getSystemIcon(context, pointerShape));
                     }
                     break;
             }
@@ -21191,42 +21191,25 @@
         }
     }
 
-    /** @hide */
-    public int getPointerShape(MotionEvent event, float x, float y) {
-        final int value = (mPrivateFlags3 & PFLAG3_POINTER_ICON_MASK);
-        switch (value) {
-            case PFLAG3_POINTER_ICON_NOT_SPECIFIED:
-                return PointerIcon.STYLE_NOT_SPECIFIED;
-            case PFLAG3_POINTER_ICON_NULL:
-                return PointerIcon.STYLE_NULL;
-            case PFLAG3_POINTER_ICON_CUSTOM:
-                return PointerIcon.STYLE_CUSTOM;
-            default:
-                return ((value - PFLAG3_POINTER_ICON_VALUE_START) >> PFLAG3_POINTER_ICON_LSHIFT)
-                        + PointerIcon.STYLE_ARROW;
-        }
+    /**
+     * Returns the pointer icon for the motion event, or null if it doesn't specify the icon.
+     * The default implementation does not care the location or event types, but some subclasses
+     * may use it (such as WebViews).
+     * @param event The MotionEvent from a mouse
+     * @param x The x position of the event, local to the view
+     * @param y The y position of the event, local to the view
+     * @see PointerIcon
+     */
+    public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
+        return mPointerIcon;
     }
 
-    /** @hide */
-    public void setPointerShape(int pointerShape) {
-        int newValue;
-        if (pointerShape == PointerIcon.STYLE_NOT_SPECIFIED) {
-            newValue = PFLAG3_POINTER_ICON_NOT_SPECIFIED;
-        } else if (pointerShape == PointerIcon.STYLE_NULL) {
-            newValue = PFLAG3_POINTER_ICON_NULL;
-        } else if (pointerShape == PointerIcon.STYLE_CUSTOM) {
-            newValue = PFLAG3_POINTER_ICON_CUSTOM;
-        } else if (pointerShape >= PointerIcon.STYLE_ARROW
-                && pointerShape <= PointerIcon.STYLE_GRABBING) {
-            newValue = ((pointerShape - PointerIcon.STYLE_ARROW) << PFLAG3_POINTER_ICON_LSHIFT)
-                    + PFLAG3_POINTER_ICON_VALUE_START;
-        } else {
-            Log.w(VIEW_LOG_TAG, "Invalid pointer shape " + pointerShape + " is specified.");
-            return;
-        }
-        if (newValue != (mPrivateFlags3 & PFLAG3_POINTER_ICON_MASK)) {
-            mPrivateFlags3 = (mPrivateFlags3 & ~PFLAG3_POINTER_ICON_MASK) | newValue;
-        }
+    /**
+     * Set the pointer icon for the current view.
+     * @param pointerIcon A PointerIcon instance which will be shown when the mouse hovers.
+     */
+    public void setPointerIcon(PointerIcon pointerIcon) {
+        mPointerIcon = pointerIcon;
     }
 
     //
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0f7d296..cd93dab 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1715,9 +1715,8 @@
         return false;
     }
 
-    /** @hide */
     @Override
-    public int getPointerShape(MotionEvent event, float x, float y) {
+    public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
         // Check what the child under the pointer says about the pointer.
         final int childrenCount = mChildrenCount;
         if (childrenCount != 0) {
@@ -1731,9 +1730,9 @@
                         ? children[childIndex] : preorderedList.get(childIndex);
                 PointF point = getLocalPoint();
                 if (isTransformedTouchPointInView(x, y, child, point)) {
-                    final int pointerShape = child.getPointerShape(event, point.x, point.y);
-                    if (pointerShape != PointerIcon.STYLE_NOT_SPECIFIED) {
-                        return pointerShape;
+                    final PointerIcon pointerIcon = child.getPointerIcon(event, point.x, point.y);
+                    if (pointerIcon != null) {
+                        return pointerIcon;
                     }
                     break;
                 }
@@ -1742,7 +1741,7 @@
 
         // The pointer is not a child or the child has no preferences, returning the default
         // implementation.
-        return super.getPointerShape(event, x, y);
+        return super.getPointerIcon(event, x, y);
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 12cf66e..faf2640 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -332,6 +332,7 @@
     private int mFpsNumFrames;
 
     private int mPointerIconShape = PointerIcon.STYLE_NOT_SPECIFIED;
+    private PointerIcon mCustomPointerIcon = null;
 
     /**
      * see {@link #playSoundEffect(int)}
@@ -4210,16 +4211,23 @@
                 final float y = event.getY();
                 if (event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT
                         && x >= 0 && x < mView.getWidth() && y >= 0 && y < mView.getHeight()) {
-                    int pointerShape = mView.getPointerShape(event, x, y);
-                    if (pointerShape == PointerIcon.STYLE_NOT_SPECIFIED) {
-                        pointerShape = PointerIcon.STYLE_DEFAULT;
-                    }
+                    PointerIcon pointerIcon = mView.getPointerIcon(event, x, y);
+                    int pointerShape = (pointerIcon != null) ?
+                            pointerIcon.getStyle() : PointerIcon.STYLE_DEFAULT;
 
-                    if (mPointerIconShape != pointerShape) {
-                        mPointerIconShape = pointerShape;
-                        final InputDevice inputDevice = event.getDevice();
-                        if (inputDevice != null) {
-                            inputDevice.setPointerShape(pointerShape);
+                    final InputDevice inputDevice = event.getDevice();
+                    if (inputDevice != null) {
+                        if (mPointerIconShape != pointerShape) {
+                            mPointerIconShape = pointerShape;
+                            if (mPointerIconShape != PointerIcon.STYLE_CUSTOM) {
+                                mCustomPointerIcon = null;
+                                inputDevice.setPointerShape(pointerShape);
+                            }
+                        }
+                        if (mPointerIconShape == PointerIcon.STYLE_CUSTOM &&
+                                !pointerIcon.equals(mCustomPointerIcon)) {
+                            mCustomPointerIcon = pointerIcon;
+                            inputDevice.setCustomPointerIcon(mCustomPointerIcon);
                         }
                     }
                 } else if (event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5574f86..17c803f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5955,15 +5955,12 @@
         return mLayout != null ? mLayout.getHeight() : 0;
     }
 
-    /**
-     * @hide
-     */
     @Override
-    public int getPointerShape(MotionEvent event, float x, float y) {
+    public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
         if (isTextSelectable() || isTextEditable()) {
-            return PointerIcon.STYLE_TEXT;
+            return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_TEXT);
         }
-        return super.getPointerShape(event, x, y);
+        return super.getPointerIcon(event, x, y);
     }
 
     @Override
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index d7e2c02..71be52e 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -65,33 +65,34 @@
         return OK;
     }
 
-    jobject loadedPointerIconObj = env->CallObjectMethod(pointerIconObj,
-            gPointerIconClassInfo.load, contextObj);
-    if (env->ExceptionCheck() || !loadedPointerIconObj) {
+    ScopedLocalRef<jobject> loadedPointerIconObj(env, env->CallObjectMethod(pointerIconObj,
+            gPointerIconClassInfo.load, contextObj));
+    if (env->ExceptionCheck() || !loadedPointerIconObj.get()) {
         ALOGW("An exception occurred while loading a pointer icon.");
         LOGW_EX(env);
         env->ExceptionClear();
         return UNKNOWN_ERROR;
     }
+    return android_view_PointerIcon_getLoadedIcon(env, loadedPointerIconObj.get(), outPointerIcon);
+}
 
-    outPointerIcon->style = env->GetIntField(loadedPointerIconObj,
-            gPointerIconClassInfo.mStyle);
-    outPointerIcon->hotSpotX = env->GetFloatField(loadedPointerIconObj,
-            gPointerIconClassInfo.mHotSpotX);
-    outPointerIcon->hotSpotY = env->GetFloatField(loadedPointerIconObj,
-            gPointerIconClassInfo.mHotSpotY);
+status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIconObj,
+        PointerIcon* outPointerIcon) {
+    outPointerIcon->style = env->GetIntField(pointerIconObj, gPointerIconClassInfo.mStyle);
+    outPointerIcon->hotSpotX = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotX);
+    outPointerIcon->hotSpotY = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotY);
 
-    jobject bitmapObj = env->GetObjectField(loadedPointerIconObj, gPointerIconClassInfo.mBitmap);
-    if (bitmapObj) {
-        GraphicsJNI::getSkBitmap(env, bitmapObj, &(outPointerIcon->bitmap));
-        env->DeleteLocalRef(bitmapObj);
+    ScopedLocalRef<jobject> bitmapObj(
+            env, env->GetObjectField(pointerIconObj, gPointerIconClassInfo.mBitmap));
+    if (bitmapObj.get()) {
+        GraphicsJNI::getSkBitmap(env, bitmapObj.get(), &(outPointerIcon->bitmap));
     }
 
     ScopedLocalRef<jobjectArray> bitmapFramesObj(env, reinterpret_cast<jobjectArray>(
-            env->GetObjectField(loadedPointerIconObj, gPointerIconClassInfo.mBitmapFrames)));
+            env->GetObjectField(pointerIconObj, gPointerIconClassInfo.mBitmapFrames)));
     if (bitmapFramesObj.get()) {
         outPointerIcon->durationPerFrame = env->GetIntField(
-                loadedPointerIconObj, gPointerIconClassInfo.mDurationPerFrame);
+                pointerIconObj, gPointerIconClassInfo.mDurationPerFrame);
         jsize size = env->GetArrayLength(bitmapFramesObj.get());
         outPointerIcon->bitmapFrames.resize(size);
         for (jsize i = 0; i < size; ++i) {
@@ -100,7 +101,6 @@
         }
     }
 
-    env->DeleteLocalRef(loadedPointerIconObj);
     return OK;
 }
 
diff --git a/core/jni/android_view_PointerIcon.h b/core/jni/android_view_PointerIcon.h
index ca08085..00bdfb4 100644
--- a/core/jni/android_view_PointerIcon.h
+++ b/core/jni/android_view_PointerIcon.h
@@ -97,6 +97,11 @@
 extern status_t android_view_PointerIcon_load(JNIEnv* env,
         jobject pointerIconObj, jobject contextObj, PointerIcon* outPointerIcon);
 
+/* Obtain the data of pointerIconObj and put to outPointerIcon. */
+extern status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIconObj,
+        PointerIcon* outPointerIcon);
+
+
 /* Loads the bitmap associated with a pointer icon by style.
  * If pointerIconObj is NULL, returns OK and a pointer icon with POINTER_ICON_STYLE_NULL. */
 extern status_t android_view_PointerIcon_loadSystemIcon(JNIEnv* env,
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 6a1167a..529849e 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -439,6 +439,17 @@
     }
 }
 
+void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
+    AutoMutex _l(mLock);
+
+    const int32_t iconId = mPolicy->getCustomPointerIconId();
+    mLocked.additionalMouseResources[iconId] = icon;
+    mLocked.requestedPointerShape = iconId;
+    mLocked.presentationChanged = true;
+
+    updatePointerLocked();
+}
+
 void PointerController::handleMessage(const Message& message) {
     switch (message.what) {
     case MSG_INACTIVITY_TIMEOUT:
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 4fd2d85..9ba37b3 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -67,6 +67,7 @@
     virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
             std::map<int32_t, PointerAnimation>* outAnimationResources) = 0;
     virtual int32_t getDefaultPointerIconId() = 0;
+    virtual int32_t getCustomPointerIconId() = 0;
 };
 
 
@@ -105,6 +106,7 @@
     virtual void clearSpots();
 
     void updatePointerShape(int32_t iconId);
+    void setCustomPointerIcon(const SpriteIcon& icon);
     void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
     void setInactivityTimeout(InactivityTimeout inactivityTimeout);
     void reloadPointerResources();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 93264ff..c01f170 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -30,6 +30,7 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.MotionEvent;
+import android.view.PointerIcon;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.View.OnTouchListener;
@@ -118,8 +119,8 @@
         updateDisplayInfo();
         boolean landscape = getResources().getConfiguration().orientation
                 == Configuration.ORIENTATION_LANDSCAPE;
-        mHandle.setPointerShape(
-                landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW);
+        mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
+                landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW));
         getViewTreeObserver().addOnComputeInternalInsetsListener(this);
     }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index f2d0031..8b37383 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -213,6 +213,7 @@
     private static native void nativeMonitor(long ptr);
     private static native void nativeSetPointerIconShape(long ptr, int iconId);
     private static native void nativeReloadPointerIcons(long ptr);
+    private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon);
 
     // Input event injection constants defined in InputDispatcher.h.
     private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
@@ -1451,6 +1452,12 @@
         nativeSetPointerIconShape(mPtr, iconId);
     }
 
+    // Binder call
+    @Override
+    public void setCustomPointerIcon(PointerIcon icon) {
+        nativeSetCustomPointerIcon(mPtr, icon);
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9c9a5da..4c8474a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -206,6 +206,7 @@
     void reloadCalibration();
     void setPointerIconShape(int32_t iconId);
     void reloadPointerIcons();
+    void setCustomPointerIcon(const SpriteIcon& icon);
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -248,6 +249,7 @@
     virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
             std::map<int32_t, PointerAnimation>* outAnimationResources);
     virtual int32_t getDefaultPointerIconId();
+    virtual int32_t getCustomPointerIconId();
 
 private:
     sp<InputManager> mInputManager;
@@ -790,6 +792,14 @@
     }
 }
 
+void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
+    AutoMutex _l(mLock);
+    sp<PointerController> controller = mLocked.pointerController.promote();
+    if (controller != NULL) {
+        controller->setCustomPointerIcon(icon);
+    }
+}
+
 TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
         JNIEnv *env, jfloatArray matrixArr) {
     ScopedFloatArrayRO matrix(env, matrixArr);
@@ -1090,6 +1100,10 @@
     return POINTER_ICON_STYLE_ARROW;
 }
 
+int32_t NativeInputManager::getCustomPointerIconId() {
+    return POINTER_ICON_STYLE_CUSTOM;
+}
+
 // ----------------------------------------------------------------------------
 
 static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
@@ -1437,6 +1451,20 @@
     im->reloadPointerIcons();
 }
 
+static void nativeSetCustomPointerIcon(JNIEnv* env, jclass /* clazz */,
+                                       jlong ptr, jobject iconObj) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    PointerIcon pointerIcon;
+    android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+
+    SpriteIcon spriteIcon;
+    pointerIcon.bitmap.copyTo(&spriteIcon.bitmap, kN32_SkColorType);
+    spriteIcon.hotSpotX = pointerIcon.hotSpotX;
+    spriteIcon.hotSpotY = pointerIcon.hotSpotY;
+    im->setCustomPointerIcon(spriteIcon);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gInputManagerMethods[] = {
@@ -1499,6 +1527,8 @@
             (void*) nativeSetPointerIconShape },
     { "nativeReloadPointerIcons", "(J)V",
             (void*) nativeReloadPointerIcons },
+    { "nativeSetCustomPointerIcon", "(JLandroid/view/PointerIcon;)V",
+            (void*) nativeSetCustomPointerIcon },
 };
 
 #define FIND_CLASS(var, className) \