Merge "Make doclava happy by returning the same type in old and new APIs."
diff --git a/api/current.xml b/api/current.xml
index 282d5c5..9605511 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -265200,9 +265200,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="beginIndex" type="int">
+<parameter name="start" type="int">
 </parameter>
-<parameter name="endIndex" type="int">
+<parameter name="end" type="int">
 </parameter>
 </method>
 <method name="ensureCapacity"
@@ -267900,7 +267900,7 @@
  native="false"
  synchronized="false"
  static="true"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -274223,7 +274223,7 @@
 </exception>
 </method>
 <method name="getClass"
- return="java.lang.Class&lt;? extends java.lang.Object&gt;"
+ return="java.lang.Class&lt;?&gt;"
  abstract="false"
  native="true"
  synchronized="false"
@@ -277013,9 +277013,9 @@
 >
 <parameter name="data" type="byte[]">
 </parameter>
-<parameter name="start" type="int">
+<parameter name="offset" type="int">
 </parameter>
-<parameter name="length" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 </constructor>
 <constructor name="String"
@@ -277029,9 +277029,9 @@
 </parameter>
 <parameter name="high" type="int">
 </parameter>
-<parameter name="start" type="int">
+<parameter name="offset" type="int">
 </parameter>
-<parameter name="length" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 </constructor>
 <constructor name="String"
@@ -277043,9 +277043,9 @@
 >
 <parameter name="data" type="byte[]">
 </parameter>
-<parameter name="start" type="int">
+<parameter name="offset" type="int">
 </parameter>
-<parameter name="length" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 <parameter name="charsetName" type="java.lang.String">
 </parameter>
@@ -277075,9 +277075,9 @@
 >
 <parameter name="data" type="byte[]">
 </parameter>
-<parameter name="start" type="int">
+<parameter name="offset" type="int">
 </parameter>
-<parameter name="length" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 <parameter name="charset" type="java.nio.charset.Charset">
 </parameter>
@@ -277113,9 +277113,9 @@
 >
 <parameter name="data" type="char[]">
 </parameter>
-<parameter name="start" type="int">
+<parameter name="offset" type="int">
 </parameter>
-<parameter name="length" type="int">
+<parameter name="charCount" type="int">
 </parameter>
 </constructor>
 <constructor name="String"
@@ -277135,7 +277135,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="stringbuffer" type="java.lang.StringBuffer">
+<parameter name="stringBuffer" type="java.lang.StringBuffer">
 </parameter>
 </constructor>
 <constructor name="String"
@@ -277159,7 +277159,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="sb" type="java.lang.StringBuilder">
+<parameter name="stringBuilder" type="java.lang.StringBuilder">
 </parameter>
 </constructor>
 <method name="charAt"
@@ -277211,9 +277211,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="beginIndex" type="int">
+<parameter name="start" type="int">
 </parameter>
-<parameter name="endIndex" type="int">
+<parameter name="end" type="int">
 </parameter>
 </method>
 <method name="compareTo"
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 14536bd..ba7f807 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -161,9 +161,9 @@
     si = find_svc(s, len);
     if (si) {
         if (si->ptr) {
-            LOGE("add_service('%s',%p) uid=%d - ALREADY REGISTERED\n",
+            LOGE("add_service('%s',%p) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
                  str8(s), ptr, uid);
-            return -1;
+            svcinfo_death(bs, si);
         }
         si->ptr = ptr;
     } else {
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index 923a555..0a097f9 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -105,10 +105,6 @@
      */
     public static final int META_SELECTING = KeyEvent.META_SELECTING;
 
-    private static final int META_SHIFT_ON_AND_LOCKED = META_SHIFT_ON | META_CAP_LOCKED;
-    private static final int META_ALT_ON_AND_LOCKED = META_ALT_ON | META_ALT_LOCKED;
-    private static final int META_SYM_ON_AND_LOCKED = META_SYM_ON | META_SYM_LOCKED;
-
     // These bits are privately used by the meta key key listener.
     // They are deliberately assigned values outside of the representable range of an 'int'
     // so as not to conflict with any meta key states publicly defined by KeyEvent.
@@ -565,13 +561,13 @@
      * {@link #META_ALT_ON} or {@link #META_SYM_ON}.
      */
     public long clearMetaKeyState(long state, int which) {
-        if ((which & META_SHIFT_ON_AND_LOCKED) == META_SHIFT_ON_AND_LOCKED) {
+        if ((which & META_SHIFT_ON) != 0 && (state & META_CAP_LOCKED) != 0) {
             state &= ~META_SHIFT_MASK;
         }
-        if ((which & META_ALT_ON_AND_LOCKED) == META_ALT_ON_AND_LOCKED) {
+        if ((which & META_ALT_ON) != 0 && (state & META_ALT_LOCKED) != 0) {
             state &= ~META_ALT_MASK;
         }
-        if ((which & META_SYM_ON_AND_LOCKED) == META_SYM_ON_AND_LOCKED) {
+        if ((which & META_SYM_ON) != 0 && (state & META_SYM_LOCKED) != 0) {
             state &= ~META_SYM_MASK;
         }
         return state;
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index e8ced9c..318c120 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -34,7 +34,6 @@
 #include "PathCache.h"
 #include "TextDropShadowCache.h"
 #include "FboCache.h"
-#include "Line.h"
 #include "ResourceCache.h"
 
 namespace android {
@@ -158,8 +157,6 @@
     GammaFontRenderer fontRenderer;
     ResourceCache resourceCache;
 
-    Line line;
-
 private:
     DebugLevel mDebugLevel;
 }; // class Caches
diff --git a/libs/hwui/Line.h b/libs/hwui/Line.h
deleted file mode 100644
index 264fd19..0000000
--- a/libs/hwui/Line.h
+++ /dev/null
@@ -1,127 +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 ANDROID_HWUI_LINE_H
-#define ANDROID_HWUI_LINE_H
-
-#include <GLES2/gl2.h>
-
-#include <cmath>
-
-#include <sys/types.h>
-
-#include "Patch.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Globals
-///////////////////////////////////////////////////////////////////////////////
-
-// Alpha8 texture used to perform texture anti-aliasing
-static const uint8_t gLineTexture[] = {
-        0,   0,   0,   0, 0,
-        0, 255, 255, 255, 0,
-        0, 255, 255, 255, 0,
-        0, 255, 255, 255, 0,
-        0,   0,   0,   0, 0
-};
-static const GLsizei gLineTextureWidth = 5;
-static const GLsizei gLineTextureHeight = 5;
-static const float gLineAABias = 1.0f;
-
-///////////////////////////////////////////////////////////////////////////////
-// Line
-///////////////////////////////////////////////////////////////////////////////
-
-class Line {
-public:
-    Line(): mXDivsCount(2), mYDivsCount(2) {
-        mPatch = new Patch(mXDivsCount, mYDivsCount);
-        mXDivs = new int32_t[mXDivsCount];
-        mYDivs = new int32_t[mYDivsCount];
-
-        mXDivs[0] = mYDivs[0] = 2;
-        mXDivs[1] = mYDivs[1] = 3;
-
-        mPatch->copy(mXDivs, mYDivs);
-
-        glGenTextures(1, &mTexture);
-        glBindTexture(GL_TEXTURE_2D, mTexture);
-
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gLineTextureWidth, gLineTextureHeight, 0,
-                GL_ALPHA, GL_UNSIGNED_BYTE, gLineTexture);
-    }
-
-    ~Line() {
-        delete mPatch;
-        delete[] mXDivs;
-        delete[] mYDivs;
-
-        glDeleteTextures(1, &mTexture);
-    }
-
-    inline float getLength(float x1, float y1, float x2, float y2) {
-        return sqrtf((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
-    }
-
-    void update(float x1, float y1, float x2, float y2, float lineWidth, float& tx, float& ty) {
-        const float length = getLength(x1, y1, x2, y2);
-        const float half = lineWidth * 0.5f;
-
-        mPatch->updateVertices(gLineTextureWidth, gLineTextureHeight,
-                -gLineAABias, -half - gLineAABias, length + gLineAABias, half + gLineAABias);
-
-        tx = -gLineAABias;
-        ty = lineWidth <= 1.0f ? -gLineAABias : -half - gLineAABias;
-    }
-
-    inline GLuint getMeshBuffer() const {
-        return mPatch->meshBuffer;
-    }
-
-    inline GLsizei getElementsCount() const {
-        return mPatch->verticesCount;
-    }
-
-    inline GLuint getTexture() const {
-        return mTexture;
-    }
-
-private:
-    uint32_t mXDivsCount;
-    uint32_t mYDivsCount;
-
-    int32_t* mXDivs;
-    int32_t* mYDivs;
-
-    Patch* mPatch;
-
-    GLuint mTexture;
-}; // class Line
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_LINE_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 1a89ca0..b357973 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -30,6 +30,7 @@
 
 #include "OpenGLRenderer.h"
 #include "DisplayListRenderer.h"
+#include "Vector.h"
 
 namespace android {
 namespace uirenderer {
@@ -989,6 +990,12 @@
     // TODO: Should do quickReject for each line
     if (mSnapshot->invisible) return;
 
+    const bool isAA = paint->isAntiAlias();
+    const float strokeWidth = paint->getStrokeWidth() * 0.5f;
+    // A stroke width of 0 has a special meaningin Skia:
+    // it draws an unscaled 1px wide line
+    const bool isHairLine = paint->getStrokeWidth() == 0.0f;
+
     setupDraw();
 
     int alpha;
@@ -1001,59 +1008,105 @@
     const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
     const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
 
-    const bool isAA = paint->isAntiAlias();
-    if (isAA) {
-        GLuint textureUnit = 0;
-        glActiveTexture(gTextureUnits[textureUnit]);
-        setupTextureAlpha8(mCaches.line.getTexture(), 0, 0, textureUnit, 0.0f, 0.0f, r, g, b, a,
-                mode, false, true, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
-                mCaches.line.getMeshBuffer());
+    // Used only with AA lines
+    GLuint textureUnit = 0;
+
+    // Describe the required shaders
+    ProgramDescription description;
+    const bool setColor = description.setColor(r, g, b, a);
+
+    if (mShader) {
+        mShader->describe(description, mCaches.extensions);
+    }
+    if (mColorFilter) {
+        mColorFilter->describe(description, mCaches.extensions);
+    }
+
+    // Setup the blending mode
+    chooseBlending(a < 1.0f || (mShader && mShader->blend()), mode, description);
+
+    // We're not drawing with VBOs here
+    mCaches.unbindMeshBuffer();
+
+    int verticesCount = count >> 2;
+    if (!isHairLine) {
+        // TODO: AA needs more vertices
+        verticesCount *= 6;
     } else {
-        setupColorRect(0.0f, 0.0f, 1.0f, 1.0f, r, g, b, a, mode, false, true);
+        // TODO: AA will be different
+        verticesCount *= 2;
     }
 
-    const float strokeWidth = paint->getStrokeWidth();
-    const GLsizei elementsCount = isAA ? mCaches.line.getElementsCount() : gMeshCount;
-    const GLenum drawMode = isAA ? GL_TRIANGLES : GL_TRIANGLE_STRIP;
+    TextureVertex lines[verticesCount];
+    TextureVertex* vertex = &lines[0];
 
-    for (int i = 0; i < count; i += 4) {
-        float tx = 0.0f;
-        float ty = 0.0f;
+    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
+            gMeshStride, vertex);
 
-        if (isAA) {
-            mCaches.line.update(points[i], points[i + 1], points[i + 2], points[i + 3],
-                    strokeWidth, tx, ty);
-        } else {
-            ty = strokeWidth <= 1.0f ? 0.0f : -strokeWidth * 0.5f;
-        }
+    mModelView.loadIdentity();
 
-        const float dx = points[i + 2] - points[i];
-        const float dy = points[i + 3] - points[i + 1];
-        const float mag = sqrtf(dx * dx + dy * dy);
-        const float angle = acos(dx / mag);
+    // Build and use the appropriate shader
+    useProgram(mCaches.programCache.get(description));
+    mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
 
-        mModelView.loadTranslate(points[i], points[i + 1], 0.0f);
-        if (angle > MIN_ANGLE || angle < -MIN_ANGLE) {
-            mModelView.rotate(angle * RAD_TO_DEG, 0.0f, 0.0f, 1.0f);
-        }
-        mModelView.translate(tx, ty, 0.0f);
-        if (!isAA) {
-            float length = mCaches.line.getLength(points[i], points[i + 1],
-                    points[i + 2], points[i + 3]);
-            mModelView.scale(length, strokeWidth, 1.0f);
-        }
-        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
-        // TODO: Add bounds to the layer's region
-
-        if (mShader) {
-            mShader->updateTransforms(mCaches.currentProgram, mModelView, *mSnapshot);
-        }
-
-        glDrawArrays(drawMode, 0, elementsCount);
+    if (!mShader || (mShader && setColor)) {
+        mCaches.currentProgram->setColor(r, g, b, a);
     }
 
-    if (isAA) {
-        glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
+    if (mShader) {
+        mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit);
+    }
+    if (mColorFilter) {
+        mColorFilter->setupProgram(mCaches.currentProgram);
+    }
+
+    if (!isHairLine) {
+        // TODO: Handle the AA case
+        for (int i = 0; i < count; i += 4) {
+            // a = start point, b = end point
+            vec2 a(points[i], points[i + 1]);
+            vec2 b(points[i + 2], points[i + 3]);
+
+            // Bias to snap to the same pixels as Skia
+            a += 0.375;
+            b += 0.375;
+
+            // Find the normal to the line
+            vec2 n = (b - a).copyNormalized() * strokeWidth;
+            float x = n.x;
+            n.x = -n.y;
+            n.y = x;
+
+            // Four corners of the rectangle defining a thick line
+            vec2 p1 = a - n;
+            vec2 p2 = a + n;
+            vec2 p3 = b + n;
+            vec2 p4 = b - n;
+
+            // Draw the line as 2 triangles, could be optimized
+            // by using only 4 vertices and the correct indices
+            // Also we should probably used non textured vertices
+            // when line AA is disabled to save on bandwidth
+            TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f);
+            TextureVertex::set(vertex++, p2.x, p2.y, 0.0f, 0.0f);
+            TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f);
+            TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f);
+            TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f);
+            TextureVertex::set(vertex++, p4.x, p4.y, 0.0f, 0.0f);
+
+            // TODO: Mark the dirty regions when RENDER_LAYERS_AS_REGIONS is set
+        }
+
+        // GL_LINE does not give the result we want to match Skia
+        glDrawArrays(GL_TRIANGLES, 0, verticesCount);
+    } else {
+        // TODO: Handle the AA case
+        for (int i = 0; i < count; i += 4) {
+            TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
+            TextureVertex::set(vertex++, points[i + 2], points[i + 3], 0.0f, 0.0f);
+        }
+        glLineWidth(1.0f);
+        glDrawArrays(GL_LINES, 0, verticesCount);
     }
 }
 
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
new file mode 100644
index 0000000..46dded5
--- /dev/null
+++ b/libs/hwui/Vector.h
@@ -0,0 +1,119 @@
+/*
+ * 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 ANDROID_HWUI_VECTOR_H
+#define ANDROID_HWUI_VECTOR_H
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
+struct Vector2 {
+    float x;
+    float y;
+
+    Vector2() :
+        x(0.0f), y(0.0f) {
+    }
+
+    Vector2(float px, float py) :
+        x(px), y(py) {
+    }
+
+    float length() const {
+        return sqrt(x * x + y * y);
+    }
+
+    void operator+=(const Vector2& v) {
+        x += v.x;
+        y += v.y;
+    }
+
+    void operator-=(const Vector2& v) {
+        x -= v.x;
+        y -= v.y;
+    }
+
+    void operator+=(const float v) {
+        x += v;
+        y += v;
+    }
+
+    void operator-=(const float v) {
+        x -= v;
+        y -= v;
+    }
+
+    void operator/=(float s) {
+        x /= s;
+        y /= s;
+    }
+
+    void operator*=(float s) {
+        x *= s;
+        y *= s;
+    }
+
+    Vector2 operator+(const Vector2& v) const {
+        return Vector2(x + v.x, y + v.y);
+    }
+
+    Vector2 operator-(const Vector2& v) const {
+        return Vector2(x - v.x, y - v.y);
+    }
+
+    Vector2 operator/(float s) const {
+        return Vector2(x / s, y / s);
+    }
+
+    Vector2 operator*(float s) const {
+        return Vector2(x * s, y * s);
+    }
+
+    void normalize() {
+        float s = 1.0f / length();
+        x *= s;
+        y *= s;
+    }
+
+    Vector2 copyNormalized() const {
+        Vector2 v(x, y);
+        v.normalize();
+        return v;
+    }
+
+    float dot(const Vector2& v) const {
+        return x * v.x + y * v.y;
+    }
+
+    void dump() {
+        LOGD("Vector2[%.2f, %.2f]", x, y);
+    }
+}; // class Vector2
+
+///////////////////////////////////////////////////////////////////////////////
+// Types
+///////////////////////////////////////////////////////////////////////////////
+
+typedef Vector2 vec2;
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_VECTOR_H
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index e61dd6c..79bf9d0 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -1344,11 +1344,11 @@
             return true;
         }
         
-        // Shortcuts are invoked through Search+key or Meta+key, so intercept those here
-        if ((mShortcutKeyPressed != -1 && !mConsumeShortcutKeyUp)
-                || (metaState & KeyEvent.META_META_ON) != 0) {
+        // Shortcuts are invoked through Search+key, so intercept those here
+        if (mShortcutKeyPressed != -1 && !mConsumeShortcutKeyUp) {
             if (down && repeatCount == 0 && !keyguardOn) {
-                Intent shortcutIntent = mShortcutManager.getIntent(event);
+                final KeyCharacterMap kcm = event.getKeyCharacterMap();
+                Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode, metaState);
                 if (shortcutIntent != null) {
                     shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                     mContext.startActivity(shortcutIntent);
@@ -1382,11 +1382,23 @@
         }
 
         if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
+            // Invoke shortcuts using Meta as a fallback.
             final KeyCharacterMap kcm = event.getKeyCharacterMap();
-            boolean fallback = kcm.getFallbackAction(event.getKeyCode(), event.getMetaState(),
-                    mFallbackAction);
+            final int keyCode = event.getKeyCode();
+            final int metaState = event.getMetaState();
+            if ((metaState & KeyEvent.META_META_ON) != 0) {
+                Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode,
+                        metaState & ~(KeyEvent.META_META_ON
+                                | KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON));
+                if (shortcutIntent != null) {
+                    shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    mContext.startActivity(shortcutIntent);
+                    return null;
+                }
+            }
 
-            if (fallback) {
+            // Check for fallback actions.
+            if (kcm.getFallbackAction(keyCode, metaState, mFallbackAction)) {
                 if (DEBUG_FALLBACK) {
                     Slog.d(TAG, "Fallback: keyCode=" + mFallbackAction.keyCode
                             + " metaState=" + Integer.toHexString(mFallbackAction.metaState));
diff --git a/policy/src/com/android/internal/policy/impl/ShortcutManager.java b/policy/src/com/android/internal/policy/impl/ShortcutManager.java
index fc66a20..75a1b01 100644
--- a/policy/src/com/android/internal/policy/impl/ShortcutManager.java
+++ b/policy/src/com/android/internal/policy/impl/ShortcutManager.java
@@ -101,18 +101,30 @@
      * This will first try an exact match (with modifiers), and then try a
      * match without modifiers (primary character on a key).
      * 
-     * @param event The key event of the key that was pressed.
+     * @param kcm The key character map of the device on which the key was pressed.
+     * @param keyCode The key code.
+     * @param metaState The meta state, omitting any modifiers that were used
+     * to invoke the shortcut.
      * @return The intent that matches the shortcut, or null if not found.
      */
-    public Intent getIntent(KeyEvent event) {
-        // First try the exact keycode (with modifiers)
-        int shortcut = event.getUnicodeChar();
-        Intent intent = shortcut != 0 ? mShortcutIntents.get(shortcut) : null; 
-        if (intent != null) return intent;
+    public Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
+        Intent intent = null;
 
-        // Next try the keycode without modifiers (the primary character on that key)
-        shortcut = Character.toLowerCase(event.getUnicodeChar(0));
-        return shortcut != 0 ? mShortcutIntents.get(shortcut) : null;
+        // First try the exact keycode (with modifiers).
+        int shortcut = kcm.get(keyCode, metaState);
+        if (shortcut != 0) {
+            intent = mShortcutIntents.get(shortcut);
+        }
+
+        // Next try the primary character on that key.
+        if (intent == null) {
+            shortcut = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
+            if (shortcut != 0) {
+                intent = mShortcutIntents.get(shortcut);
+            }
+        }
+
+        return intent;
     }
 
 }
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 7f3a968..7fa71a9 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -261,8 +261,7 @@
 
         <activity
                 android:name="LinesActivity"
-                android:label="_Lines"
-                android:hardwareAccelerated="true">
+                android:label="_Lines">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
index 0dc836d..6afbb01 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
@@ -36,6 +36,8 @@
     }
 
     static class LinesView extends View {
+        private static final boolean LINE_AA = false;
+
         private final Bitmap mBitmap1;
         private final Paint mSmallPaint;
         private final Paint mMediumPaint;
@@ -43,6 +45,7 @@
         private final BitmapShader mShader;
         private final float[] mPoints;
         private final Paint mAlphaPaint;
+        private final Paint mHairLinePaint;
 
         LinesView(Context c) {
             super(c);
@@ -50,25 +53,30 @@
             mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
 
             mSmallPaint = new Paint();
-            mSmallPaint.setAntiAlias(true);
+            mSmallPaint.setAntiAlias(LINE_AA);
             mSmallPaint.setColor(0xffff0000);
             mSmallPaint.setStrokeWidth(1.0f);
 
             mMediumPaint = new Paint();
-            mMediumPaint.setAntiAlias(true);
+            mMediumPaint.setAntiAlias(LINE_AA);
             mMediumPaint.setColor(0xff0000ff);
             mMediumPaint.setStrokeWidth(4.0f);
 
             mLargePaint = new Paint();
-            mLargePaint.setAntiAlias(true);
+            mLargePaint.setAntiAlias(LINE_AA);
             mLargePaint.setColor(0xff00ff00);
             mLargePaint.setStrokeWidth(15.0f);
 
             mAlphaPaint = new Paint();
-            mAlphaPaint.setAntiAlias(true);
+            mAlphaPaint.setAntiAlias(LINE_AA);
             mAlphaPaint.setColor(0x7fff0050);
             mAlphaPaint.setStrokeWidth(10.0f);
             
+            mHairLinePaint = new Paint();
+            mHairLinePaint.setAntiAlias(LINE_AA);
+            mHairLinePaint.setColor(0xff0000ff);
+            mHairLinePaint.setStrokeWidth(0.0f);
+
             mShader = new BitmapShader(mBitmap1, BitmapShader.TileMode.MIRROR,
                     BitmapShader.TileMode.MIRROR);
 
@@ -100,7 +108,7 @@
 
             mSmallPaint.setAntiAlias(false);
             canvas.drawLine(0.0f, 0.0f, 400.0f, 0.0f, mSmallPaint);
-            mSmallPaint.setAntiAlias(true);
+            mSmallPaint.setAntiAlias(LINE_AA);
             canvas.drawLine(0.0f, 0.0f, 0.0f, 400.0f, mSmallPaint);
             canvas.drawLine(0.0f, 400.0f, 400.0f, 400.0f, mSmallPaint);
             
@@ -110,6 +118,12 @@
             mAlphaPaint.setShader(null);
 
             canvas.restore();
+            
+            canvas.scale(10.0f, 10.0f);
+            canvas.drawLine(50.0f, 40.0f, 10.0f, 40.0f, mSmallPaint);
+            canvas.drawLine(10.0f, 50.0f, 50.0f, 50.0f, mSmallPaint);
+            canvas.drawLine(10.0f, 60.0f, 50.0f, 60.0f, mHairLinePaint);
+            canvas.restore();
         }
     }
 }