Merge "Initialize reference counter for egl_display_t"
diff --git a/Android.mk b/Android.mk
index aae10dc..3902971 100644
--- a/Android.mk
+++ b/Android.mk
@@ -370,6 +370,7 @@
     -since ./frameworks/base/api/9.xml 9 \
     -since ./frameworks/base/api/10.xml 10 \
     -since ./frameworks/base/api/11.xml 11 \
+    -since ./frameworks/base/api/12.xml 12 \
 		-werror -hide 113 \
 		-overview $(LOCAL_PATH)/core/java/overview.html
 
diff --git a/api/current.xml b/api/current.xml
index 555f804..58aa1fd 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -9013,6 +9013,28 @@
  visibility="public"
 >
 </field>
+<field name="state_drag_can_accept"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843621"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="state_drag_hovered"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843622"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="state_empty"
  type="int"
  transient="false"
@@ -15681,6 +15703,17 @@
  visibility="public"
 >
 </field>
+<field name="Theme_Holo_Light_NoActionBar"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16974064"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Theme_Holo_Light_Panel"
  type="int"
  transient="false"
@@ -259865,7 +259898,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="appWidgetId" type="int">
@@ -259875,6 +259908,21 @@
 <parameter name="intent" type="android.content.Intent">
 </parameter>
 </method>
+<method name="setRemoteAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
 <method name="setScrollPosition"
  return="void"
  abstract="false"
@@ -267668,7 +267716,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3e5b21f..cb07135 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -916,7 +916,7 @@
         public static final int HIDE_WINDOW             = 106;
         public static final int RESUME_ACTIVITY         = 107;
         public static final int SEND_RESULT             = 108;
-        public static final int DESTROY_ACTIVITY         = 109;
+        public static final int DESTROY_ACTIVITY        = 109;
         public static final int BIND_APPLICATION        = 110;
         public static final int EXIT_APPLICATION        = 111;
         public static final int NEW_INTENT              = 112;
@@ -1130,8 +1130,8 @@
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
         }
 
-        void maybeSnapshot() {
-            if (mBoundApplication != null) {
+        private void maybeSnapshot() {
+            if (mBoundApplication != null && SamplingProfilerIntegration.isEnabled()) {
                 // convert the *private* ActivityThread.PackageInfo to *public* known
                 // android.content.pm.PackageInfo
                 String packageName = mBoundApplication.info.mPackageName;
@@ -3396,8 +3396,7 @@
     }
         
     final void handleLowMemory() {
-        ArrayList<ComponentCallbacks> callbacks
-                = new ArrayList<ComponentCallbacks>();
+        ArrayList<ComponentCallbacks> callbacks;
 
         synchronized (mPackages) {
             callbacks = collectComponentCallbacksLocked(true, null);
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 31119d7..72fa07c 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -57,11 +57,30 @@
      */
     public boolean userSetLocale;
 
+    /** Constant for {@link #screenLayout}: bits that encode the size. */
     public static final int SCREENLAYOUT_SIZE_MASK = 0x0f;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
+     * value indicating that no size has been set. */
     public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0x00;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
+     * value indicating the screen is at least approximately 320x426 dp units.
+     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information. */
     public static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
+     * value indicating the screen is at least approximately 320x470 dp units.
+     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information. */
     public static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
+     * value indicating the screen is at least approximately 480x640 dp units.
+     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information. */
     public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
+    /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
+     * value indicating the screen is at least approximately 720x960 dp units.
+     * See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information.*/
     public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
     
     public static final int SCREENLAYOUT_LONG_MASK = 0x30;
@@ -88,6 +107,9 @@
      * <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
      * is wider/taller than normal.  They may be one of
      * {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.
+     * 
+     * <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
+     * Multiple Screens</a> for more information.
      */
     public int screenLayout;
     
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 0c6ab9e..c8cb1de 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -95,7 +95,7 @@
      * Defines the UID/GID for the NFC service process.
      * @hide
      */
-    public static final int NFC_UID = 1022;
+    public static final int NFC_UID = 1023;
 
     /**
      * Defines the GID for the group that allows write access to the internal media storage.
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index d426d124..6e2168b 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -58,6 +58,13 @@
             int flags, float[] advances, int advancesIndex, Paint paint);
 
     /**
+     * Just like {@link Paint#getTextRunAdvances}.
+     * @hide
+     */
+    float getTextRunAdvancesICU(int start, int end, int contextStart, int contextEnd,
+            int flags, float[] advances, int advancesIndex, Paint paint);
+
+    /**
      * Just like {@link Paint#getTextRunCursor}.
      * @hide
      */
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index ea5cdfe..ff6a4cd 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1170,6 +1170,35 @@
     }
 
     /**
+     * Don't call this yourself -- exists for Paint to use internally.
+     * {@hide}
+     */
+    public float getTextRunAdvancesICU(int start, int end, int contextStart, int contextEnd, int flags,
+            float[] advances, int advancesPos, Paint p) {
+
+        float ret;
+
+        int contextLen = contextEnd - contextStart;
+        int len = end - start;
+
+        if (end <= mGapStart) {
+            ret = p.getTextRunAdvancesICU(mText, start, len, contextStart, contextLen,
+                    flags, advances, advancesPos);
+        } else if (start >= mGapStart) {
+            ret = p.getTextRunAdvancesICU(mText, start + mGapLength, len,
+                    contextStart + mGapLength, contextLen, flags, advances, advancesPos);
+        } else {
+            char[] buf = TextUtils.obtain(contextLen);
+            getChars(contextStart, contextEnd, buf, 0);
+            ret = p.getTextRunAdvancesICU(buf, start - contextStart, len,
+                    0, contextLen, flags, advances, advancesPos);
+            TextUtils.recycle(buf);
+        }
+
+        return ret;
+    }
+
+    /**
      * Returns the next cursor position in the run.  This avoids placing the cursor between
      * surrogates, between characters that form conjuncts, between base characters and combining
      * marks, or within a reordering cluster.
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index f6aeb39..03189ca 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -67,6 +67,15 @@
      */
     public abstract void setSource(int source);
 
+    /**
+     * Recycles the event.
+     * This method should only be used by the system since applications do not
+     * expect {@link KeyEvent} objects to be recycled, although {@link MotionEvent}
+     * objects are fine.  See {@link KeyEvent#recycle()} for details.
+     * @hide
+     */
+    public abstract void recycle();
+
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 96cddfa..0ef56cc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1305,6 +1305,8 @@
     static final int VIEW_STATE_ACTIVATED = 1 << 5;
     static final int VIEW_STATE_ACCELERATED = 1 << 6;
     static final int VIEW_STATE_HOVERED = 1 << 7;
+    static final int VIEW_STATE_DRAG_CAN_ACCEPT = 1 << 8;
+    static final int VIEW_STATE_DRAG_HOVERED = 1 << 9;
 
     static final int[] VIEW_STATE_IDS = new int[] {
         R.attr.state_window_focused,    VIEW_STATE_WINDOW_FOCUSED,
@@ -1315,6 +1317,8 @@
         R.attr.state_activated,         VIEW_STATE_ACTIVATED,
         R.attr.state_accelerated,       VIEW_STATE_ACCELERATED,
         R.attr.state_hovered,           VIEW_STATE_HOVERED,
+        R.attr.state_drag_can_accept,   VIEW_STATE_DRAG_CAN_ACCEPT,
+        R.attr.state_drag_hovered,      VIEW_STATE_DRAG_HOVERED,
     };
 
     static {
@@ -1651,6 +1655,27 @@
      */
     static final int INVALIDATED                  = 0x80000000;
 
+    /* Masks for mPrivateFlags2 */
+
+    /**
+     * Indicates that this view has reported that it can accept the current drag's content.
+     * Cleared when the drag operation concludes.
+     * @hide
+     */
+    static final int DRAG_CAN_ACCEPT              = 0x00000001;
+
+    /**
+     * Indicates that this view is currently directly under the drag location in a
+     * drag-and-drop operation involving content that it can accept.  Cleared when
+     * the drag exits the view, or when the drag operation concludes.
+     * @hide
+     */
+    static final int DRAG_HOVERED                 = 0x00000002;
+
+    /* End of masks for mPrivateFlags2 */
+
+    static final int DRAG_MASK = DRAG_CAN_ACCEPT | DRAG_HOVERED;
+
     /**
      * Always allow a user to over-scroll this view, provided it is a
      * view that can scroll.
@@ -1822,6 +1847,7 @@
         @ViewDebug.FlagToString(mask = DIRTY_MASK, equals = DIRTY, name = "DIRTY")
     })
     int mPrivateFlags;
+    int mPrivateFlags2;
 
     /**
      * This view's request for the visibility of the status bar.
@@ -2251,12 +2277,6 @@
     private ViewPropertyAnimator mAnimator = null;
 
     /**
-     * Cache drag/drop state
-     *
-     */
-    boolean mCanAcceptDrop;
-
-    /**
      * Flag indicating that a drag can cross window boundaries.  When
      * {@link #startDrag(ClipData, DragShadowBuilder, Object, int)} is called
      * with this flag set, all visible applications will be able to participate
@@ -10035,6 +10055,10 @@
         }
         if ((privateFlags & HOVERED) != 0) viewStateIndex |= VIEW_STATE_HOVERED;
 
+        final int privateFlags2 = mPrivateFlags2;
+        if ((privateFlags2 & DRAG_CAN_ACCEPT) != 0) viewStateIndex |= VIEW_STATE_DRAG_CAN_ACCEPT;
+        if ((privateFlags2 & DRAG_HOVERED) != 0) viewStateIndex |= VIEW_STATE_DRAG_HOVERED;
+
         drawableState = VIEW_STATE_SETS[viewStateIndex];
 
         //noinspection ConstantIfStatement
@@ -11701,6 +11725,10 @@
         return onDragEvent(event);
     }
 
+    boolean canAcceptDrag() {
+        return (mPrivateFlags2 & DRAG_CAN_ACCEPT) != 0;
+    }
+
     /**
      * This needs to be a better API (NOT ON VIEW) before it is exposed.  If
      * it is ever exposed at all.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 058b826..f7f2685 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -929,6 +929,7 @@
             final View[] children = mChildren;
             for (int i = 0; i < count; i++) {
                 final View child = children[i];
+                child.mPrivateFlags2 &= ~View.DRAG_MASK;
                 if (child.getVisibility() == VISIBLE) {
                     final boolean handled = notifyChildOfDrag(children[i]);
                     if (handled) {
@@ -949,6 +950,8 @@
                 for (View child : mDragNotifiedChildren) {
                     // If a child was notified about an ongoing drag, it's told that it's over
                     child.dispatchDragEvent(event);
+                    child.mPrivateFlags2 &= ~View.DRAG_MASK;
+                    child.refreshDrawableState();
                 }
 
                 mDragNotifiedChildren.clear();
@@ -979,8 +982,11 @@
                 final int action = event.mAction;
                 // If we've dragged off of a child view, send it the EXITED message
                 if (mCurrentDragView != null) {
+                    final View view = mCurrentDragView;
                     event.mAction = DragEvent.ACTION_DRAG_EXITED;
-                    mCurrentDragView.dispatchDragEvent(event);
+                    view.dispatchDragEvent(event);
+                    view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
+                    view.refreshDrawableState();
                 }
                 mCurrentDragView = target;
 
@@ -988,6 +994,8 @@
                 if (target != null) {
                     event.mAction = DragEvent.ACTION_DRAG_ENTERED;
                     target.dispatchDragEvent(event);
+                    target.mPrivateFlags2 |= View.DRAG_HOVERED;
+                    target.refreshDrawableState();
                 }
                 event.mAction = action;  // restore the event's original state
             }
@@ -1018,7 +1026,11 @@
 
         case DragEvent.ACTION_DRAG_EXITED: {
             if (mCurrentDragView != null) {
-                mCurrentDragView.dispatchDragEvent(event);
+                final View view = mCurrentDragView;
+                view.dispatchDragEvent(event);
+                view.mPrivateFlags2 &= ~View.DRAG_HOVERED;
+                view.refreshDrawableState();
+
                 mCurrentDragView = null;
             }
         } break;
@@ -1056,7 +1068,7 @@
         final View[] children = mChildren;
         for (int i = count - 1; i >= 0; i--) {
             final View child = children[i];
-            if (!child.mCanAcceptDrop) {
+            if (!child.canAcceptDrag()) {
                 continue;
             }
 
@@ -1072,11 +1084,16 @@
             Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
         }
 
+        boolean canAccept = false;
         if (! mDragNotifiedChildren.contains(child)) {
             mDragNotifiedChildren.add(child);
-            child.mCanAcceptDrop = child.dispatchDragEvent(mCurrentDrag);
+            canAccept = child.dispatchDragEvent(mCurrentDrag);
+            if (canAccept && !child.canAcceptDrag()) {
+                child.mPrivateFlags2 |= View.DRAG_CAN_ACCEPT;
+                child.refreshDrawableState();
+            }
         }
-        return child.mCanAcceptDrop;
+        return canAccept;
     }
 
     @Override
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 334c68e..9d00d02 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -81,6 +81,8 @@
 
     public final static int FLAG_INJECTED = 0x01000000;
     public final static int FLAG_TRUSTED = 0x02000000;
+    public final static int FLAG_FILTERED = 0x04000000;
+    public final static int FLAG_DISABLE_KEY_REPEAT = 0x08000000;
 
     public final static int FLAG_WOKE_HERE = 0x10000000;
     public final static int FLAG_BRIGHT_HERE = 0x20000000;
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 3ce0730..e21a02e 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -857,6 +857,7 @@
         String cacheControl = headers.getCacheControl();
         if (cacheControl != null) {
             String[] controls = cacheControl.toLowerCase().split("[ ,;]");
+            boolean noCache = false;
             for (int i = 0; i < controls.length; i++) {
                 if (NO_STORE.equals(controls[i])) {
                     return null;
@@ -867,7 +868,12 @@
                 // can only be used in CACHE_MODE_CACHE_ONLY case
                 if (NO_CACHE.equals(controls[i])) {
                     ret.expires = 0;
-                } else if (controls[i].startsWith(MAX_AGE)) {
+                    noCache = true;
+                // if cache control = no-cache has been received, ignore max-age
+                // header, according to http spec:
+                // If a request includes the no-cache directive, it SHOULD NOT
+                // include min-fresh, max-stale, or max-age.
+                } else if (controls[i].startsWith(MAX_AGE) && !noCache) {
                     int separator = controls[i].indexOf('=');
                     if (separator < 0) {
                         separator = controls[i].indexOf(':');
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 9636513..3d15968 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -198,8 +198,6 @@
 
         if (mProgressView != null) {
             mProgressView.setVisibility(View.GONE);
-            mLayout.removeView(mProgressView);
-            mProgressView = null;
         }
 
         mVideoWidth = mp.getVideoWidth();
@@ -321,4 +319,13 @@
         return false;
     }
 
+    @Override
+    protected void switchProgressView(boolean playerBuffering) {
+        if (playerBuffering) {
+            mProgressView.setVisibility(View.VISIBLE);
+        } else {
+            mProgressView.setVisibility(View.GONE);
+        }
+        return;
+    }
 }
diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java
index 8ea73b5..fd3f358 100644
--- a/core/java/android/webkit/HTML5VideoView.java
+++ b/core/java/android/webkit/HTML5VideoView.java
@@ -15,7 +15,7 @@
 /**
  * @hide This is only used by the browser
  */
-public class HTML5VideoView implements MediaPlayer.OnPreparedListener{
+public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
 
     protected static final String LOGTAG = "HTML5VideoView";
 
@@ -78,6 +78,7 @@
                         TIMEUPDATE_PERIOD);
             }
             mPlayer.start();
+            setPlayerBuffering(false);
         }
     }
 
@@ -189,6 +190,10 @@
         mPlayer.setOnPreparedListener(this);
     }
 
+    public void setOnInfoListener(HTML5VideoViewProxy proxy) {
+        mPlayer.setOnInfoListener(proxy);
+    }
+
     // Normally called immediately after setVideoURI. But for full screen,
     // this should be after surface holder created
     public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
@@ -198,7 +203,7 @@
         setOnCompletionListener(proxy);
         setOnPreparedListener(proxy);
         setOnErrorListener(proxy);
-
+        setOnInfoListener(proxy);
         // When there is exception, we could just bail out silently.
         // No Video will be played though. Write the stack for debug
         try {
@@ -292,4 +297,21 @@
         return 0;
     }
 
+    // This is true only when the player is buffering and paused
+    public boolean mPlayerBuffering = false;
+
+    public boolean getPlayerBuffering() {
+        return mPlayerBuffering;
+    }
+
+    public void setPlayerBuffering(boolean playerBuffering) {
+        mPlayerBuffering = playerBuffering;
+        switchProgressView(playerBuffering);
+    }
+
+
+    protected void switchProgressView(boolean playerBuffering) {
+        // Only used in HTML5VideoFullScreen
+    }
+
 }
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index acd7eab..14157c2 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -46,6 +46,7 @@
                           implements MediaPlayer.OnPreparedListener,
                           MediaPlayer.OnCompletionListener,
                           MediaPlayer.OnErrorListener,
+                          MediaPlayer.OnInfoListener,
                           SurfaceTexture.OnFrameAvailableListener {
     // Logging tag.
     private static final String LOGTAG = "HTML5VideoViewProxy";
@@ -56,6 +57,8 @@
     private static final int PAUSE               = 102;
     private static final int ERROR               = 103;
     private static final int LOAD_DEFAULT_POSTER = 104;
+    private static final int BUFFERING_START     = 105;
+    private static final int BUFFERING_END       = 106;
 
     // Message Ids to be handled on the WebCore thread
     private static final int PREPARED          = 200;
@@ -92,6 +95,10 @@
         // identify the exact layer on the UI thread to use the SurfaceTexture.
         private static int mBaseLayer = 0;
 
+        private static void setPlayerBuffering(boolean playerBuffering) {
+            mHTML5VideoView.setPlayerBuffering(playerBuffering);
+        }
+
         // Every time webView setBaseLayer, this will be called.
         // When we found the Video layer, then we set the Surface Texture to it.
         // Otherwise, we may want to delete the Surface Texture to save memory.
@@ -106,6 +113,8 @@
                 int currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
                 if (layer != 0 && surfTexture != null && currentVideoLayerId != -1) {
                     int playerState = mHTML5VideoView.getCurrentState();
+                    if (mHTML5VideoView.getPlayerBuffering())
+                        playerState = HTML5VideoView.STATE_NOTPREPARED;
                     boolean foundInTree = nativeSendSurfaceTexture(surfTexture,
                             layer, currentVideoLayerId, textureName,
                             playerState);
@@ -159,7 +168,6 @@
                 WebChromeClient client, int videoLayerId) {
             int currentVideoLayerId = -1;
             boolean backFromFullScreenMode = false;
-
             if (mHTML5VideoView != null) {
                 currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
                 if (mHTML5VideoView instanceof HTML5VideoFullScreen) {
@@ -341,6 +349,14 @@
                 }
                 break;
             }
+            case BUFFERING_START: {
+                VideoPlayer.setPlayerBuffering(true);
+                break;
+            }
+            case BUFFERING_END: {
+                VideoPlayer.setPlayerBuffering(false);
+                break;
+            }
         }
     }
 
@@ -670,4 +686,14 @@
     private native static boolean nativeSendSurfaceTexture(SurfaceTexture texture,
             int baseLayer, int videoLayerId, int textureName,
             int playerState);
+
+    @Override
+    public boolean onInfo(MediaPlayer mp, int what, int extra) {
+        if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
+            sendMessage(obtainMessage(BUFFERING_START, what, extra));
+        } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
+            sendMessage(obtainMessage(BUFFERING_END, what, extra));
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index c854fac..9cf2718 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -125,7 +125,7 @@
      *  SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
      */
     private abstract static class Action implements Parcelable {
-        public abstract void apply(View root) throws ActionException;
+        public abstract void apply(View root, ViewGroup rootParent) throws ActionException;
 
         public int describeContents() {
             return 0;
@@ -183,7 +183,7 @@
         }
 
         @Override
-        public void apply(View root) {
+        public void apply(View root, ViewGroup rootParent) {
             final View view = root.findViewById(viewId);
             if (!(view instanceof AdapterView<?>)) return;
 
@@ -214,7 +214,7 @@
         }
 
         @Override
-        public void apply(View root) {
+        public void apply(View root, ViewGroup rootParent) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -295,7 +295,7 @@
         }
 
         @Override
-        public void apply(View root) {
+        public void apply(View root, ViewGroup rootParent) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -360,6 +360,60 @@
         public final static int TAG = 8;
     }
 
+    private class SetRemoteViewsAdapterIntent extends Action {
+        public SetRemoteViewsAdapterIntent(int id, Intent intent) {
+            this.viewId = id;
+            this.intent = intent;
+        }
+
+        public SetRemoteViewsAdapterIntent(Parcel parcel) {
+            viewId = parcel.readInt();
+            intent = Intent.CREATOR.createFromParcel(parcel);
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(TAG);
+            dest.writeInt(viewId);
+            intent.writeToParcel(dest, flags);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent) {
+            final View target = root.findViewById(viewId);
+            if (target == null) return;
+
+            // Ensure that we are applying to an AppWidget root
+            if (!(rootParent instanceof AppWidgetHostView)) {
+                Log.e("RemoteViews", "SetRemoteViewsAdapterIntent action can only be used for " +
+                        "AppWidgets (root id: " + viewId + ")");
+                return;
+            }
+            // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it
+            if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) {
+                Log.e("RemoteViews", "Cannot setRemoteViewsAdapter on a view which is not " +
+                        "an AbsListView or AdapterViewAnimator (id: " + viewId + ")");
+                return;
+            }
+
+            // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
+            // RemoteViewsService
+            AppWidgetHostView host = (AppWidgetHostView) rootParent;
+            intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
+            if (target instanceof AbsListView) {
+                AbsListView v = (AbsListView) target;
+                v.setRemoteViewsAdapter(intent);
+            } else if (target instanceof AdapterViewAnimator) {
+                AdapterViewAnimator v = (AdapterViewAnimator) target;
+                v.setRemoteViewsAdapter(intent);
+            }
+        }
+
+        int viewId;
+        Intent intent;
+
+        public final static int TAG = 10;
+    }
+
     /**
      * Equivalent to calling
      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
@@ -383,7 +437,7 @@
         }
 
         @Override
-        public void apply(View root) {
+        public void apply(View root, ViewGroup rootParent) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
 
@@ -479,7 +533,7 @@
         }
         
         @Override
-        public void apply(View root) {
+        public void apply(View root, ViewGroup rootParent) {
             final View target = root.findViewById(viewId);
             if (target == null) return;
             
@@ -539,7 +593,7 @@
         }
 
         @Override
-        public void apply(View root) {
+        public void apply(View root, ViewGroup rootParent) {
             final View view = root.findViewById(viewId);
             if (view == null) return;
 
@@ -755,7 +809,7 @@
         }
 
         @Override
-        public void apply(View root) {
+        public void apply(View root, ViewGroup rootParent) {
             final View view = root.findViewById(viewId);
             if (view == null) return;
 
@@ -850,7 +904,7 @@
         }
 
         @Override
-        public void apply(View root) {
+        public void apply(View root, ViewGroup rootParent) {
             final Context context = root.getContext();
             final ViewGroup target = (ViewGroup) root.findViewById(viewId);
             if (target == null) return;
@@ -952,6 +1006,9 @@
                 case SetOnClickFillInIntent.TAG:
                     mActions.add(new SetOnClickFillInIntent(parcel));
                     break;
+                case SetRemoteViewsAdapterIntent.TAG:
+                    mActions.add(new SetRemoteViewsAdapterIntent(parcel));
+                    break;
                 default:
                     throw new ActionException("Tag " + tag + " not found");
                 }
@@ -1287,16 +1344,29 @@
     /**
      * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
      *
-     * @param appWidgetId The id of the app widget which contains the specified view
+     * @param appWidgetId The id of the app widget which contains the specified view. (This
+     *      parameter is ignored in this deprecated method)
+     * @param viewId The id of the view whose text should change
+     * @param intent The intent of the service which will be
+     *            providing data to the RemoteViewsAdapter
+     * @deprecated This method has been deprecated. See
+     *      {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)}
+     */
+    @Deprecated
+    public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
+        setRemoteAdapter(viewId, intent);
+    }
+
+    /**
+     * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
+     * Can only be used for App Widgets.
+     *
      * @param viewId The id of the view whose text should change
      * @param intent The intent of the service which will be
      *            providing data to the RemoteViewsAdapter
      */
-    public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) {
-        // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
-        // RemoteViewsService
-        intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, appWidgetId);
-        setIntent(viewId, "setRemoteViewsAdapter", intent);
+    public void setRemoteAdapter(int viewId, Intent intent) {
+        addAction(new SetRemoteViewsAdapterIntent(viewId, intent));
     }
 
     /**
@@ -1499,7 +1569,7 @@
 
         result = inflater.inflate(mLayoutId, parent, false);
 
-        performApply(result);
+        performApply(result, parent);
 
         return result;
     }
@@ -1514,15 +1584,15 @@
      */
     public void reapply(Context context, View v) {
         prepareContext(context);
-        performApply(v);
+        performApply(v, (ViewGroup) v.getParent());
     }
 
-    private void performApply(View v) {
+    private void performApply(View v, ViewGroup parent) {
         if (mActions != null) {
             final int count = mActions.size();
             for (int i = 0; i < count; i++) {
                 Action a = mActions.get(i);
-                a.apply(v);
+                a.apply(v, parent);
             }
         }
     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index baf20a1..9e482b4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2951,6 +2951,16 @@
                     advancesIndex);
         }
 
+        public float getTextRunAdvancesICU(int start, int end, int contextStart,
+                int contextEnd, int flags, float[] advances, int advancesIndex,
+                Paint p) {
+            int count = end - start;
+            int contextCount = contextEnd - contextStart;
+            return p.getTextRunAdvancesICU(mChars, start + mStart, count,
+                    contextStart + mStart, contextCount, flags, advances,
+                    advancesIndex);
+        }
+
         public int getTextRunCursor(int contextStart, int contextEnd, int flags,
                 int offset, int cursorOpt, Paint p) {
             int contextCount = contextEnd - contextStart;
diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
index 268a9d4..df0fcd9 100644
--- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java
+++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
@@ -20,13 +20,15 @@
 import android.os.Build;
 import android.os.SystemProperties;
 import android.util.Log;
-import dalvik.system.profiler.AsciiHprofWriter;
+import dalvik.system.profiler.BinaryHprofWriter;
 import dalvik.system.profiler.SamplingProfiler;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.PrintStream;
+import java.util.Date;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
@@ -81,7 +83,8 @@
         }
     }
 
-    private static SamplingProfiler INSTANCE;
+    private static SamplingProfiler samplingProfiler;
+    private static long startMillis;
 
     /**
      * Is profiling enabled?
@@ -97,10 +100,16 @@
         if (!enabled) {
             return;
         }
+        if (samplingProfiler != null) {
+            Log.e(TAG, "SamplingProfilerIntegration already started at " + new Date(startMillis));
+            return;
+        }
+
         ThreadGroup group = Thread.currentThread().getThreadGroup();
         SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group);
-        INSTANCE = new SamplingProfiler(samplingProfilerDepth, threadSet);
-        INSTANCE.start(samplingProfilerMilliseconds);
+        samplingProfiler = new SamplingProfiler(samplingProfilerDepth, threadSet);
+        samplingProfiler.start(samplingProfilerMilliseconds);
+        startMillis = System.currentTimeMillis();
     }
 
     /**
@@ -110,6 +119,10 @@
         if (!enabled) {
             return;
         }
+        if (samplingProfiler == null) {
+            Log.e(TAG, "SamplingProfilerIntegration is not started");
+            return;
+        }
 
         /*
          * If we're already writing a snapshot, don't bother enqueueing another
@@ -138,8 +151,9 @@
             return;
         }
         writeSnapshotFile("zygote", null);
-        INSTANCE.shutdown();
-        INSTANCE = null;
+        samplingProfiler.shutdown();
+        samplingProfiler = null;
+        startMillis = 0;
     }
 
     /**
@@ -149,40 +163,44 @@
         if (!enabled) {
             return;
         }
-        INSTANCE.stop();
+        samplingProfiler.stop();
 
         /*
-         * We use the current time as a unique ID. We can't use a counter
-         * because processes restart. This could result in some overlap if
-         * we capture two snapshots in rapid succession.
+         * We use the global start time combined with the process name
+         * as a unique ID. We can't use a counter because processes
+         * restart. This could result in some overlap if we capture
+         * two snapshots in rapid succession.
          */
-        long start = System.currentTimeMillis();
         String name = processName.replaceAll(":", ".");
-        String path = SNAPSHOT_DIR + "/" + name + "-" +System.currentTimeMillis() + ".snapshot";
-        PrintStream out = null;
+        String path = SNAPSHOT_DIR + "/" + name + "-" + startMillis + ".snapshot";
+        long start = System.currentTimeMillis();
+        OutputStream outputStream = null;
         try {
-            out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path)));
+            outputStream = new BufferedOutputStream(new FileOutputStream(path));
+            PrintStream out = new PrintStream(outputStream);
             generateSnapshotHeader(name, packageInfo, out);
-            new AsciiHprofWriter(INSTANCE.getHprofData(), out).write();
             if (out.checkError()) {
                 throw new IOException();
             }
+            BinaryHprofWriter.write(samplingProfiler.getHprofData(), outputStream);
         } catch (IOException e) {
             Log.e(TAG, "Error writing snapshot to " + path, e);
             return;
         } finally {
-            IoUtils.closeQuietly(out);
+            IoUtils.closeQuietly(outputStream);
         }
         // set file readable to the world so that SamplingProfilerService
         // can put it to dropbox
         new File(path).setReadable(true, false);
 
         long elapsed = System.currentTimeMillis() - start;
-        Log.i(TAG, "Wrote snapshot for " + name + " in " + elapsed + "ms.");
+        Log.i(TAG, "Wrote snapshot " + path + " in " + elapsed + "ms.");
+        samplingProfiler.start(samplingProfilerMilliseconds);
     }
 
     /**
-     * generate header for snapshots, with the following format (like http header):
+     * generate header for snapshots, with the following format
+     * (like an HTTP header but without the \r):
      *
      * Version: <version number of profiler>\n
      * Process: <process name>\n
@@ -195,7 +213,7 @@
     private static void generateSnapshotHeader(String processName, PackageInfo packageInfo,
             PrintStream out) {
         // profiler version
-        out.println("Version: 2");
+        out.println("Version: 3");
         out.println("Process: " + processName);
         if (packageInfo != null) {
             out.println("Package: " + packageInfo.packageName);
diff --git a/core/jni/android/graphics/HarfbuzzSkia.cpp b/core/jni/android/graphics/HarfbuzzSkia.cpp
index 58fb32b..92c743f 100644
--- a/core/jni/android/graphics/HarfbuzzSkia.cpp
+++ b/core/jni/android/graphics/HarfbuzzSkia.cpp
@@ -34,6 +34,8 @@
 #include "SkRect.h"
 #include "SkTypeface.h"
 
+#include <utils/Log.h>
+
 extern "C" {
 #include "harfbuzz-shaper.h"
 }
@@ -43,20 +45,13 @@
 
 namespace android {
 
-static HB_Fixed SkiaScalarToHarfbuzzFixed(SkScalar value)
-{
-    // HB_Fixed is a 26.6 fixed point format.
-    return value * 64;
-}
-
 static void setupPaintWithFontData(SkPaint* paint, FontData* data) {
-    paint->setAntiAlias(true);
-    paint->setSubpixelText(true);
-    paint->setHinting(SkPaint::kSlight_Hinting);
-    paint->setTextSize(SkFloatToScalar(data->textSize));
     paint->setTypeface(data->typeFace);
-    paint->setFakeBoldText(data->fakeBold);
-    paint->setTextSkewX(data->fakeItalic ? -SK_Scalar1/4 : 0);
+    paint->setTextSize(data->textSize);
+    paint->setTextSkewX(data->textSkewX);
+    paint->setTextScaleX(data->textScaleX);
+    paint->setFlags(data->flags);
+    paint->setHinting(data->hinting);
 }
 
 static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length,
@@ -67,16 +62,13 @@
     setupPaintWithFontData(&paint, data);
 
     paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
-    int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t),
-            reinterpret_cast<uint16_t*>(glyphs));
+    uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs);
+    int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
 
     // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
     // |glyphs| array needs to be converted.
     for (int i = numGlyphs - 1; i >= 0; --i) {
-        uint16_t value;
-        // We use a memcpy to avoid breaking strict aliasing rules.
-        memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(value));
-        glyphs[i] = value;
+        glyphs[i] = skiaGlyphs[i];
     }
 
     *glyphsSize = numGlyphs;
@@ -97,16 +89,17 @@
         return;
     for (unsigned i = 0; i < numGlyphs; ++i)
         glyphs16[i] = glyphs[i];
-    paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), reinterpret_cast<SkScalar*>(advances));
+    SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances);
+    paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
 
     // The |advances| values which Skia outputs are SkScalars, which are floats
     // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
     // These two formats are both 32-bits long.
     for (unsigned i = 0; i < numGlyphs; ++i) {
-        float value;
-        // We use a memcpy to avoid breaking strict aliasing rules.
-        memcpy(&value, reinterpret_cast<char*>(advances) + sizeof(float) * i, sizeof(value));
-        advances[i] = SkiaScalarToHarfbuzzFixed(value);
+        advances[i] = SkScalarToHBFixed(scalarAdvances[i]);
+#if DEBUG_ADVANCES
+        LOGD("glyphsToAdvances -- advances[%d]=%d", i, advances[i]);
+#endif
     }
     delete glyphs16;
 }
@@ -156,8 +149,8 @@
         return HB_Err_Invalid_SubTable;
     // Skia does let us get a single point from the path.
     path.getPoints(points, point + 1);
-    *xPos = SkiaScalarToHarfbuzzFixed(points[point].fX);
-    *yPos = SkiaScalarToHarfbuzzFixed(points[point].fY);
+    *xPos = SkScalarToHBFixed(points[point].fX);
+    *yPos = SkScalarToHBFixed(points[point].fY);
     *resultingNumPoints = numPoints;
     delete points;
 
@@ -176,12 +169,12 @@
     SkRect bounds;
     paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
 
-    metrics->x = SkiaScalarToHarfbuzzFixed(bounds.fLeft);
-    metrics->y = SkiaScalarToHarfbuzzFixed(bounds.fTop);
-    metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width());
-    metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height());
+    metrics->x = SkScalarToHBFixed(bounds.fLeft);
+    metrics->y = SkScalarToHBFixed(bounds.fTop);
+    metrics->width = SkScalarToHBFixed(bounds.width());
+    metrics->height = SkScalarToHBFixed(bounds.height());
 
-    metrics->xOffset = SkiaScalarToHarfbuzzFixed(width);
+    metrics->xOffset = SkScalarToHBFixed(width);
     // We can't actually get the |y| correct because Skia doesn't export
     // the vertical advance. However, nor we do ever render vertical text at
     // the moment so it's unimportant.
@@ -199,7 +192,7 @@
 
     switch (metric) {
     case HB_FontAscent:
-        return SkiaScalarToHarfbuzzFixed(-skiaMetrics.fAscent);
+        return SkScalarToHBFixed(-skiaMetrics.fAscent);
     // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them.
     default:
         return 0;
diff --git a/core/jni/android/graphics/HarfbuzzSkia.h b/core/jni/android/graphics/HarfbuzzSkia.h
index d057d76..99b389a 100644
--- a/core/jni/android/graphics/HarfbuzzSkia.h
+++ b/core/jni/android/graphics/HarfbuzzSkia.h
@@ -27,22 +27,38 @@
 #ifndef HarfbuzzSkia_h
 #define HarfbuzzSkia_h
 
+#include "SkScalar.h"
 #include "SkTypeface.h"
+#include "SkPaint.h"
 
 extern "C" {
 #include "harfbuzz-shaper.h"
 }
 
 namespace android {
-    typedef struct {
-        SkTypeface* typeFace;
-        float textSize;
-        bool fakeBold;
-        bool fakeItalic;
-    } FontData;
 
-    HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
-    extern const HB_FontClass harfbuzzSkiaClass;
+static inline float HBFixedToFloat(HB_Fixed v) {
+    // Harfbuzz uses 26.6 fixed point values for pixel offsets
+    return v * (1.0f / 64);
+}
+
+static inline HB_Fixed SkScalarToHBFixed(SkScalar value) {
+    // HB_Fixed is a 26.6 fixed point format.
+    return SkScalarToFloat(value) * 64.0f;
+}
+
+typedef struct {
+    SkTypeface* typeFace;
+    SkScalar textSize;
+    SkScalar textSkewX;
+    SkScalar textScaleX;
+    uint32_t flags;
+    SkPaint::Hinting hinting;
+} FontData;
+
+HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
+extern const HB_FontClass harfbuzzSkiaClass;
+
 }  // namespace android
 
 #endif
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 5c3497f..27be871 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -414,6 +414,7 @@
         for (int i = 0; i < glyphCount; i++) {
             glyphsArray[i] = (jchar) shaperItem.glyphs[i];
         }
+        env->ReleaseCharArrayElements(glyphs, glyphsArray, JNI_ABORT);
         return glyphCount;
     }
 
@@ -442,6 +443,21 @@
         return totalAdvance;
     }
 
+    static jfloat doTextRunAdvancesICU(JNIEnv *env, SkPaint *paint, const jchar *text,
+                                    jint start, jint count, jint contextCount, jint flags,
+                                    jfloatArray advances, jint advancesIndex) {
+        jfloat advancesArray[count];
+        jfloat totalAdvance;
+
+        TextLayout::getTextRunAdvancesICU(paint, text, start, count, contextCount, flags,
+                                       advancesArray, totalAdvance);
+
+        if (advances != NULL) {
+            env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray);
+        }
+        return totalAdvance;
+    }
+
     static float getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
             jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
             jint flags, jfloatArray advances, jint advancesIndex) {
@@ -463,6 +479,27 @@
         return result;
     }
 
+    static float getTextRunAdvancesICU___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
+            jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
+            jint flags, jfloatArray advances, jint advancesIndex) {
+        jchar* textArray = env->GetCharArrayElements(text, NULL);
+        jfloat result = doTextRunAdvancesICU(env, paint, textArray + contextIndex,
+            index - contextIndex, count, contextCount, flags, advances, advancesIndex);
+        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+        return result;
+    }
+
+    static float getTextRunAdvancesICU__StringIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
+            jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
+            jfloatArray advances, jint advancesIndex) {
+        const jchar* textArray = env->GetStringChars(text, NULL);
+        jfloat result = doTextRunAdvancesICU(env, paint, textArray + contextStart,
+            start - contextStart, end - start, contextEnd - contextStart, flags, advances,
+            advancesIndex);
+        env->ReleaseStringChars(text, textArray);
+        return result;
+    }
+
     static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start,
             jint count, jint flags, jint offset, jint opt) {
         SkScalar scalarArray[count];
@@ -748,10 +785,14 @@
     {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS},
     {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F},
     {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F},
-    {"native_getTextRunAdvances","(I[CIIIII[FI)F", (void*)
-        SkPaintGlue::getTextRunAdvances___CIIIII_FI},
+    {"native_getTextRunAdvances","(I[CIIIII[FI)F",
+        (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FI},
     {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F",
         (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI},
+    {"native_getTextRunAdvancesICU","(I[CIIIII[FI)F",
+        (void*) SkPaintGlue::getTextRunAdvancesICU___CIIIII_FI},
+    {"native_getTextRunAdvancesICU","(ILjava/lang/String;IIIII[FI)F",
+        (void*) SkPaintGlue::getTextRunAdvancesICU__StringIIIII_FI},
     {"native_getTextGlyphs","(ILjava/lang/String;IIIII[C)I",
         (void*) SkPaintGlue::getTextGlyphs__StringIIIII_C},
     {"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
diff --git a/core/jni/android/graphics/RtlProperties.h b/core/jni/android/graphics/RtlProperties.h
index 2c68fa3..f41f4a1 100644
--- a/core/jni/android/graphics/RtlProperties.h
+++ b/core/jni/android/graphics/RtlProperties.h
@@ -45,7 +45,12 @@
     return kRtlDebugDisabled;
 }
 
+// Define if we want to use Harfbuzz (1) or not (0)
 #define RTL_USE_HARFBUZZ 1
 
+// Define if we want (1) to have Advances debug values or not (0)
+#define DEBUG_ADVANCES 0
+
+
 } // namespace android
 #endif // ANDROID_RTL_PROPERTIES_H
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index f1bb696..434f63b 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -269,6 +269,21 @@
 #endif
 }
 
+void TextLayout::getTextRunAdvancesHB(SkPaint* paint, const jchar* chars, jint start,
+                                    jint count, jint contextCount, jint dirFlags,
+                                    jfloat* resultAdvances, jfloat& resultTotalAdvance) {
+    // Compute advances and return them
+    RunAdvanceDescription::computeAdvancesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
+            resultAdvances, &resultTotalAdvance);
+}
+
+void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start,
+                                    jint count, jint contextCount, jint dirFlags,
+                                    jfloat* resultAdvances, jfloat& resultTotalAdvance) {
+    // Compute advances and return them
+    RunAdvanceDescription::computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags,
+            resultAdvances, &resultTotalAdvance);
+}
 
 // Draws a paragraph of text on a single line, running bidi and shaping
 void TextLayout::drawText(SkPaint* paint, const jchar* text, jsize len,
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index a950d13..138983c 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -73,6 +73,14 @@
                                    jint count, jint contextCount, jint dirFlags,
                                    jfloat* resultAdvances, jfloat& resultTotalAdvance);
 
+    static void getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start,
+                                   jint count, jint contextCount, jint dirFlags,
+                                   jfloat* resultAdvances, jfloat& resultTotalAdvance);
+
+    static void getTextRunAdvancesHB(SkPaint* paint, const jchar* chars, jint start,
+                                   jint count, jint contextCount, jint dirFlags,
+                                   jfloat* resultAdvances, jfloat& resultTotalAdvance);
+
     static void drawText(SkPaint* paint, const jchar* text, jsize len,
                          jint bidiFlags, jfloat x, jfloat y, SkCanvas* canvas);
 
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 925bb7c..bced40c 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -19,17 +19,20 @@
 
 #include "RtlProperties.h"
 
-#include "stddef.h"
+#include <stddef.h>
 #include <utils/threads.h>
 #include <utils/String16.h>
-#include "utils/GenerationCache.h"
-#include "utils/Compare.h"
+#include <utils/GenerationCache.h>
+#include <utils/Compare.h>
 
-#include "SkPaint.h"
-#include "SkTemplates.h"
+#include <SkPaint.h>
+#include <SkTemplates.h>
+#include <SkUtils.h>
+#include <SkScalerContext.h>
+#include <SkAutoKern.h>
 
-#include "unicode/ubidi.h"
-#include "unicode/ushape.h"
+#include <unicode/ubidi.h>
+#include <unicode/ushape.h>
 #include "HarfbuzzSkia.h"
 #include "harfbuzz-shaper.h"
 
@@ -54,21 +57,16 @@
 // Define the interval in number of cache hits between two statistics dump
 #define DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL 100
 
-// Define if we want to have Advances debug values
-#define DEBUG_ADVANCES 0
-
 namespace android {
 
-// Harfbuzz uses 26.6 fixed point values for pixel offsets
-#define HB_FIXED_TO_FLOAT(v) (((float) v) * (1.0 / 64))
-
 /**
  * TextLayoutCacheKey is the Cache key
  */
 class TextLayoutCacheKey {
 public:
     TextLayoutCacheKey() : text(NULL), start(0), count(0), contextCount(0),
-            dirFlags(0), textSize(0), typeface(NULL), textSkewX(0), fakeBoldText(false)  {
+            dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
+            hinting(SkPaint::kNo_Hinting)  {
     }
 
     TextLayoutCacheKey(const SkPaint* paint,
@@ -76,22 +74,28 @@
             size_t contextCount, int dirFlags) :
                 text(text), start(start), count(count), contextCount(contextCount),
                 dirFlags(dirFlags) {
-        textSize = paint->getTextSize();
         typeface = paint->getTypeface();
+        textSize = paint->getTextSize();
         textSkewX = paint->getTextSkewX();
-        fakeBoldText = paint->isFakeBoldText();
+        textScaleX = paint->getTextScaleX();
+        flags = paint->getFlags();
+        hinting = paint->getHinting();
     }
 
     bool operator<(const TextLayoutCacheKey& rhs) const {
         LTE_INT(count) {
             LTE_INT(contextCount) {
                 LTE_INT(start) {
-                    LTE_FLOAT(textSize) {
-                        LTE_INT(typeface) {
-                            LTE_INT(textSkewX) {
-                                LTE_INT(fakeBoldText) {
-                                    LTE_INT(dirFlags) {
-                                        return strncmp16(text, rhs.text, contextCount) < 0;
+                    LTE_INT(typeface) {
+                        LTE_FLOAT(textSize) {
+                            LTE_FLOAT(textSkewX) {
+                                LTE_FLOAT(textScaleX) {
+                                    LTE_INT(flags) {
+                                        LTE_INT(hinting) {
+                                            LTE_INT(dirFlags) {
+                                                return strncmp16(text, rhs.text, contextCount) < 0;
+                                            }
+                                        }
                                     }
                                 }
                             }
@@ -124,10 +128,12 @@
     size_t count;
     size_t contextCount;
     int dirFlags;
-    float textSize;
     SkTypeface* typeface;
-    float textSkewX;
-    bool fakeBoldText;
+    SkScalar textSize;
+    SkScalar textSkewX;
+    SkScalar textScaleX;
+    uint32_t flags;
+    SkPaint::Hinting hinting;
 }; // TextLayoutCacheKey
 
 /*
@@ -202,6 +208,8 @@
         shaperItem->font = font;
         shaperItem->face = HB_NewFace(shaperItem->font, harfbuzzSkiaGetTable);
 
+        shaperItem->kerning_applied = false;
+
         // We cannot know, ahead of time, how many glyphs a given script run
         // will produce. We take a guess that script runs will not produce more
         // than twice as many glyphs as there are code points plus a bit of
@@ -222,10 +230,12 @@
         shaperItem->string = chars;
         shaperItem->stringLength = contextCount;
 
-        fontData->textSize = paint->getTextSize();
-        fontData->fakeBold = paint->isFakeBoldText();
-        fontData->fakeItalic = (paint->getTextSkewX() > 0);
         fontData->typeFace = paint->getTypeface();
+        fontData->textSize = paint->getTextSize();
+        fontData->textSkewX = paint->getTextSkewX();
+        fontData->textScaleX = paint->getTextScaleX();
+        fontData->flags = paint->getFlags();
+        fontData->hinting = paint->getHinting();
 
         shaperItem->font->userData = fontData;
     }
@@ -261,13 +271,15 @@
                 contextCount, dirFlags);
 
 #if DEBUG_ADVANCES
-        LOGD("HARFBUZZ -- num_glypth=%d", shaperItem.num_glyphs);
+        LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs, shaperItem.kerning_applied);
+        LOGD("         -- string= '%s'", String8(chars, contextCount).string());
+        LOGD("         -- isDevKernText=%d", paint->isDevKernText());
 #endif
 
         jfloat totalAdvance = 0;
+
         for (size_t i = 0; i < count; i++) {
-            // Be careful: we need to use ceilf() for doing the same way as what Skia is doing
-            totalAdvance += outAdvances[i] = ceilf(HB_FIXED_TO_FLOAT(shaperItem.advances[i]));
+            totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]);
 
 #if DEBUG_ADVANCES
             LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i],
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0c9a2ef..348ded7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1031,7 +1031,7 @@
     <permission android:name="android.permission.STOP_APP_SWITCHES"
         android:label="@string/permlab_stopAppSwitches"
         android:description="@string/permdesc_stopAppSwitches"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signatureOrSystem" />
 
     <!-- Allows an application to retrieve the current state of keys and
          switches.  This is only for use by the system.-->
diff --git a/core/res/res/drawable-hdpi/ic_media_video_poster.png b/core/res/res/drawable-hdpi/ic_media_video_poster.png
index 6c1fd6b..77b6b0e 100644
--- a/core/res/res/drawable-hdpi/ic_media_video_poster.png
+++ b/core/res/res/drawable-hdpi/ic_media_video_poster.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/password_keyboard_background_holo.9.png b/core/res/res/drawable-hdpi/password_keyboard_background_holo.9.png
new file mode 100644
index 0000000..c56c704
--- /dev/null
+++ b/core/res/res/drawable-hdpi/password_keyboard_background_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_bg_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_bg_activated_holo_dark.9.png
new file mode 100644
index 0000000..a233b0d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_bg_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_bg_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_bg_default_holo_dark.9.png
new file mode 100644
index 0000000..403f502
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_bg_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_bg_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_bg_disabled_focused_holo_dark.9.png
new file mode 100644
index 0000000..0ded801
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_bg_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_bg_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_bg_disabled_holo_dark.9.png
new file mode 100644
index 0000000..27237b8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_bg_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_bg_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_bg_focused_holo_dark.9.png
new file mode 100644
index 0000000..0e451f1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_bg_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_media_video_poster.png b/core/res/res/drawable-ldpi/ic_media_video_poster.png
index 786d0e6..7b34913 100644
--- a/core/res/res/drawable-ldpi/ic_media_video_poster.png
+++ b/core/res/res/drawable-ldpi/ic_media_video_poster.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_video_poster.png b/core/res/res/drawable-mdpi/ic_media_video_poster.png
old mode 100644
new mode 100755
index 10bbd74..f457f23
--- a/core/res/res/drawable-mdpi/ic_media_video_poster.png
+++ b/core/res/res/drawable-mdpi/ic_media_video_poster.png
Binary files differ
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index 4d8c688..aac0853 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -16,59 +16,81 @@
 ** limitations under the License.
 */
 -->
+
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:gravity="center_horizontal">
+    android:orientation="vertical">
 
-    <LinearLayout
+    <View
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal">
-        <!-- "Enter PIN(Password) to unlock" -->
-        <TextView android:id="@+id/status1"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:orientation="horizontal"
-            android:layout_marginRight="6dip"
-            android:layout_marginLeft="6dip"
-            android:layout_marginTop="10dip"
-            android:layout_marginBottom="10dip"
-            android:gravity="left"
-            android:ellipsize="marquee"
-            android:text="@android:string/keyguard_password_enter_password_code"
-            android:textAppearance="?android:attr/textAppearanceLarge"
-        />
-
-        <!-- Password entry field -->
-        <EditText android:id="@+id/passwordEntry"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:singleLine="true"
-            android:textStyle="bold"
-            android:inputType="textPassword"
-            android:gravity="center"
-            android:layout_gravity="center"
-            android:textSize="24sp"
-            android:textAppearance="?android:attr/textAppearanceLarge"
-            android:background="@drawable/password_field_default"
-            android:textColor="#ffffffff"
-        />
-    </LinearLayout>
-
-    <!-- Alphanumeric keyboard -->
-    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="#00000000"
-        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+        android:layout_height="0dip"
+        android:layout_weight="1"
     />
 
-    <!-- emergency call button -->
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <!-- left side: status -->
+        <include layout="@layout/keyguard_screen_status_land"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_alignParentLeft="true"/>
+
+        <!-- right side: password -->
+        <LinearLayout
+            android:layout_width="300dip"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true">
+
+            <!-- Password entry field -->
+            <EditText android:id="@+id/passwordEntry"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:singleLine="true"
+                android:textStyle="normal"
+                android:inputType="textPassword"
+                android:gravity="center"
+                android:textSize="24sp"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:background="@drawable/lockscreen_password_field_dark"
+                android:textColor="#ffffffff"
+                />
+
+            <!-- Numeric keyboard -->
+            <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+                android:layout_width="300dip"
+                android:layout_height="400dip"
+                android:background="#40000000"
+                android:layout_marginTop="5dip"
+                android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+                android:visibility="gone"
+            />
+        </LinearLayout>
+
+    </RelativeLayout>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+    />
+
+    <!-- Alphanumeric keyboard -->
+    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboardAlpha"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/password_keyboard_background_holo"
+        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+        android:keyTextSize="18dip"
+        android:visibility="gone"
+    />
+
+    <!-- emergency call button NOT CURRENTLY USED -->
     <Button
         android:id="@+id/emergencyCall"
         android:layout_width="wrap_content"
@@ -76,6 +98,7 @@
         android:drawableLeft="@drawable/ic_emergency"
         android:drawablePadding="8dip"
         android:text="@string/lockscreen_emergency_call"
+        android:visibility="gone"
         style="@style/Widget.Button.Transparent"
     />
 
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index 93966de..7805672b 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -19,63 +19,18 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:gravity="center_horizontal">
+    android:orientation="vertical">
 
-    <!-- "Enter PIN(Password) to unlock" -->
-    <TextView android:id="@+id/status1"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:layout_marginRight="6dip"
-        android:layout_marginLeft="6dip"
-        android:layout_marginTop="10dip"
-        android:layout_marginBottom="10dip"
-        android:gravity="center"
-        android:text="@android:string/keyguard_password_enter_password_code"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-    />
-
-    <!-- spacer above text entry field -->
-    <View
-        android:id="@+id/spacerBottom"
-        android:layout_width="fill_parent"
-        android:layout_height="1dip"
-        android:layout_marginTop="6dip"
-        android:background="@android:drawable/divider_horizontal_dark"
-    />
-
-    <!-- Password entry field -->
-    <EditText android:id="@+id/passwordEntry"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:singleLine="true"
-        android:textStyle="bold"
-        android:inputType="textPassword"
-        android:gravity="center"
-        android:layout_gravity="center"
-        android:textSize="32sp"
-        android:layout_marginTop="15dip"
-        android:layout_marginLeft="30dip"
-        android:layout_marginRight="30dip"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:background="@drawable/password_field_default"
-        android:textColor="#ffffffff"
-    />
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1" />
-
-    <!-- Alphanumeric keyboard -->
-    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="#00000000"
-        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
-    />
+    <!-- top: status -->
+    <RelativeLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+        <include layout="@layout/keyguard_screen_status_port"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentLeft="true"/>
+    </RelativeLayout>
 
     <!-- emergency call button -->
     <Button
@@ -84,9 +39,48 @@
         android:layout_height="wrap_content"
         android:drawableLeft="@drawable/ic_emergency"
         android:drawablePadding="8dip"
-        android:layout_marginTop="20dip"
-        android:layout_marginBottom="20dip"
         android:text="@string/lockscreen_emergency_call"
+        android:visibility="gone"
         style="@style/Widget.Button.Transparent"
     />
-</LinearLayout>
+
+    <!-- bottom: password -->
+
+    <!-- Password entry field -->
+    <EditText android:id="@+id/passwordEntry"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:singleLine="true"
+        android:textStyle="normal"
+        android:inputType="textPassword"
+        android:gravity="center"
+        android:textSize="22sp"
+        android:background="@drawable/lockscreen_password_field_dark"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="#ffffffff"/>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+    />
+
+    <!-- Numeric keyboard -->
+    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+        android:layout_width="match_parent"
+        android:layout_height="260dip"
+        android:background="#40000000"
+        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+    />
+
+    <!-- Alphanumeric keyboard -->
+    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboardAlpha"
+        android:layout_width="match_parent"
+        android:layout_height="400dip"
+        android:background="@drawable/password_keyboard_background_holo"
+        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+        android:keyTextSize="22dip"
+        android:visibility="gone"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/keyguard_screen_status_land.xml b/core/res/res/layout/keyguard_screen_status_land.xml
new file mode 100644
index 0000000..259a3af
--- /dev/null
+++ b/core/res/res/layout/keyguard_screen_status_land.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- Status to show on the left side of lock screen -->
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="left"
+        >
+
+    <TextView
+        android:id="@+id/carrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="17sp"
+        android:drawablePadding="4dip"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        />
+
+    <com.android.internal.widget.DigitalClock android:id="@+id/time"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true"
+        android:layout_marginTop="8dip"
+        android:layout_marginBottom="8dip">
+
+        <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
+        top of the other. Hence the redundant layout... -->
+        <TextView android:id="@+id/timeDisplayBackground"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:ellipsize="none"
+            android:textSize="40sp"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/lockscreen_clock_background"
+            android:layout_marginBottom="6dip"
+            />
+
+        <TextView android:id="@+id/timeDisplayForeground"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:ellipsize="none"
+            android:textSize="40sp"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/lockscreen_clock_foreground"
+            android:layout_alignLeft="@id/timeDisplayBackground"
+            android:layout_alignTop="@id/timeDisplayBackground"
+            android:layout_marginBottom="6dip"
+            />
+
+        <TextView android:id="@+id/am_pm"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toRightOf="@id/timeDisplayBackground"
+            android:layout_alignBaseline="@id/timeDisplayBackground"
+            android:singleLine="true"
+            android:ellipsize="none"
+            android:textSize="18sp"
+            android:layout_marginLeft="8dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/lockscreen_clock_am_pm"
+            />
+
+    </com.android.internal.widget.DigitalClock>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/time"
+        android:layout_marginTop="10dip">
+
+        <TextView
+            android:id="@+id/date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textSize="17sp"/>
+
+        <TextView
+            android:id="@+id/alarm_status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="30dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textSize="17sp"/>
+
+    </LinearLayout>
+
+    <!-- Status2 is generally charge status  -->
+    <TextView
+        android:id="@+id/status2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="17sp"
+        android:layout_marginTop="10dip"
+        android:drawablePadding="4dip"
+        android:visibility="gone"
+        />
+
+    <!-- Status1 is generally battery status and informational messages -->
+    <TextView
+        android:id="@+id/status1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dip"
+        android:textSize="17sp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        />
+
+    <TextView
+        android:id="@+id/propertyOf"
+        android:lineSpacingExtra="8dip"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="17sp"
+        android:layout_marginTop="20dip"
+        android:singleLine="false"
+        android:textColor="@color/lockscreen_owner_info"
+        android:visibility="gone"
+        />
+</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_status_port.xml b/core/res/res/layout/keyguard_screen_status_port.xml
new file mode 100644
index 0000000..680c073
--- /dev/null
+++ b/core/res/res/layout/keyguard_screen_status_port.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- Status to show on the left side of lock screen -->
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="left">
+
+    <TextView
+        android:id="@+id/carrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="17sp"
+        android:drawablePadding="4dip"
+        android:layout_marginTop="10dip"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        />
+
+    <com.android.internal.widget.DigitalClock android:id="@+id/time"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dip"
+        android:layout_marginBottom="8dip">
+
+        <!-- Because we can't have multi-tone fonts, we render two TextViews, one on
+        top of the other. Hence the redundant layout... -->
+        <TextView android:id="@+id/timeDisplayBackground"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:ellipsize="none"
+            android:textSize="60sp"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/lockscreen_clock_background"
+            android:layout_marginBottom="6dip"
+            />
+
+        <TextView android:id="@+id/timeDisplayForeground"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:ellipsize="none"
+            android:textSize="60sp"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/lockscreen_clock_foreground"
+            android:layout_marginBottom="6dip"
+            android:layout_alignLeft="@id/timeDisplayBackground"
+            android:layout_alignTop="@id/timeDisplayBackground"
+            />
+
+        <TextView android:id="@+id/am_pm"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toRightOf="@id/timeDisplayBackground"
+            android:layout_alignBaseline="@id/timeDisplayBackground"
+            android:singleLine="true"
+            android:ellipsize="none"
+            android:textSize="22sp"
+            android:layout_marginLeft="8dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/lockscreen_clock_am_pm"
+            />
+
+    </com.android.internal.widget.DigitalClock>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/time"
+        android:layout_marginTop="10dip">
+
+        <TextView
+            android:id="@+id/date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textSize="17sp"/>
+
+        <TextView
+            android:id="@+id/alarm_status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="30dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textSize="17sp"/>
+
+    </LinearLayout>
+
+    <!-- used for status such as the next alarm, and charging status.  -->
+    <TextView
+        android:id="@+id/status2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="17sp"
+        android:layout_marginTop="10dip"
+        android:drawablePadding="4dip"
+        android:visibility="gone"
+        />
+
+    <TextView
+        android:id="@+id/status1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dip"
+        android:textSize="17sp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        />
+
+    <TextView
+        android:id="@+id/propertyOf"
+        android:lineSpacingExtra="8dip"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20dip"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="17sp"
+        android:singleLine="false"
+        android:visibility="gone"
+        android:textColor="@color/lockscreen_owner_info"
+        />
+</LinearLayout>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index fbfc3bf..058daa8 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -1,25 +1,29 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* 
+/*
 **
 ** Copyright 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 
+** 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 
+**     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 
+** 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.
 */
 -->
 
 <resources>
-    <dimen name="password_keyboard_key_height">47dip</dimen>
+    <!-- Default height of a key in the password keyboard for alpha -->
+    <dimen name="password_keyboard_key_height_alpha">47dip</dimen>
+    <!-- Default height of a key in the password keyboard for numeric -->
+    <dimen name="password_keyboard_key_height_numeric">60dip</dimen>
+    <!-- Default correction for the space key in the password keyboard -->
     <dimen name="password_keyboard_spacebar_vertical_correction">2dip</dimen>
     <dimen name="preference_screen_side_margin">96dp</dimen>
     <dimen name="preference_screen_side_margin_negative">-100dp</dimen>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e2c440a..77f4e01 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3818,6 +3818,8 @@
          <li>"state_grow"
          <li>"state_move"
          <li>"state_hovered"
+         <li>"state_drag_can_accept"
+         <li>"state_drag_hovered"
          </ul>  -->
     <declare-styleable name="DrawableStates">
         <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
@@ -3870,6 +3872,14 @@
         <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
              set when a pointer is hovering over the view. -->
         <attr name="state_hovered" format="boolean" />
+        <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable}
+             indicating that the Drawable is in a view that is capable of accepting a drop of
+             the content currently being manipulated in a drag-and-drop operation. -->
+        <attr name="state_drag_can_accept" format="boolean" />
+        <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable}
+             indicating that a drag operation (for which the Drawable's view is a valid recipient)
+             is currently positioned over the Drawable. -->
+        <attr name="state_drag_hovered" format="boolean" />
     </declare-styleable>
     <declare-styleable name="ViewDrawableStates">
         <attr name="state_pressed" />
@@ -3880,6 +3890,8 @@
         <attr name="state_activated" />
         <attr name="state_accelerated" />
         <attr name="state_hovered" />
+        <attr name="state_drag_can_accept" />
+        <attr name="state_drag_hovered" />
     </declare-styleable>
     <!-- State array representing a menu item that is currently checked. -->
     <declare-styleable name="MenuItemCheckedState">
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index cca7d8b..968d99c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -34,6 +34,8 @@
     <dimen name="status_bar_height">25dip</dimen>
     <!-- Height of the status bar -->
     <dimen name="status_bar_icon_size">25dip</dimen>
+    <!-- Size of the giant number (unread count) in the notifications -->
+    <dimen name="status_bar_content_number_size">48sp</dimen>
     <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
     <dimen name="status_bar_edge_ignore">5dp</dimen>
     <!-- Margin for permanent screen decorations at the bottom. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5432212..4109ae1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1654,5 +1654,9 @@
      =============================================================== -->
   <eat-comment />
   <public type="attr" name="state_hovered" />
+  <public type="attr" name="state_drag_can_accept" />
+  <public type="attr" name="state_drag_hovered" />
+
+  <public type="style" name="Theme.Holo.Light.NoActionBar" />
 
 </resources>
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 51a8b27..ef0a4e6 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -287,8 +287,8 @@
 key SPACE {
     label:                              ' '
     base:                               ' '
-    ctrl, alt:                          none
-    meta:                               fallback SEARCH
+    ctrl:                               none
+    alt, meta:                          fallback SEARCH
 }
 
 key ENTER {
@@ -300,8 +300,8 @@
 key TAB {
     label:                              '\t'
     base:                               '\t'
-    ctrl, alt:                          none
-    meta:                               fallback APP_SWITCH
+    ctrl:                               none
+    alt, meta:                          fallback APP_SWITCH
 }
 
 key COMMA {
@@ -542,8 +542,8 @@
 
 key ESCAPE {
     base:                               fallback BACK
-    meta:                               fallback HOME
-    alt:                                fallback MENU
+    alt, meta:                          fallback HOME
+    ctrl:                               fallback MENU
 }
 
 ### Gamepad buttons ###
diff --git a/docs/html/guide/market/billing/billing_integrate.jd b/docs/html/guide/market/billing/billing_integrate.jd
index 26bda66..56e471e 100755
--- a/docs/html/guide/market/billing/billing_integrate.jd
+++ b/docs/html/guide/market/billing/billing_integrate.jd
@@ -296,7 +296,7 @@
 <pre>
 try {
   boolean bindResult = mContext.bindService(
-    new Intent(IMarketBillingService.class.getName()), this, Context.BIND_AUTO_CREATE);
+    new Intent("com.android.vending.billing.MarketBillingService.BIND"), this, Context.BIND_AUTO_CREATE);
   if (bindResult) {
     Log.i(TAG, "Service bind successful.");
   } else {
diff --git a/docs/html/guide/practices/screens_support.jd b/docs/html/guide/practices/screens_support.jd
index 520bd28..9875a6e 100644
--- a/docs/html/guide/practices/screens_support.jd
+++ b/docs/html/guide/practices/screens_support.jd
@@ -189,12 +189,32 @@
 handle the actual rendering of the UI on the current device screen according to
 its characteristics. </p>
 
-
 <img src="{@docRoot}images/screens_support/screens-ranges.png" />
 <p class="img-caption"><strong>Figure 1.</strong>
 Illustration of how the Android platform maps actual screen densities and sizes
 to generalized density and size configurations. </p>
 
+<p>Layout designs often need to be done against a minimum amount of
+available space, so each screen size bucket has an associated minimum size.
+These sizes are in "dp" units -- the same units you should use in defining
+your layouts, which allow us to avoid worrying about changes in screen density.</p>
+
+<ul>
+<li> <em>xlarge</em> screens are at least 960dp x 720dp.
+<li> <em>large</em> screens are at least 640dp x 480dp.
+<li> <em>normal</em> screens are at least 470dp x 320dp.
+<li> <em>small</em> screens are at least 426dp x 320dp.
+</ul>
+
+<p>Note that these minimum screen sizes were not
+as well defined prior to Android 3.0, so you may encounter some devices
+that are mis-classified between normal and large.  These are also based
+on the physical resolution of the screen, so may vary across devices --
+for example a 1024x720 tablet with a system bar would actually have a bit
+less space available to the application due to it being used by the system
+bar.  Android does not currently support screens smaller than the "small"
+426dp x 320dp size.</p>
+
 <p>Although the platform lets your application provide customized resources for
 the various size and density configurations, you do not need to do write
 custom code or provide custom resources for every combination of screen size and density.
@@ -212,7 +232,8 @@
 screen.</p>
 
 <p class="table-caption" id="screens-table"><strong>Table 1.</strong> Screen
-sizes and densities of emulator skins included in the Android SDK.</p>
+sizes and densities of emulator skins included in the Android SDK and other
+representative resolutions.</p>
 
   <table>
     <tbody>
@@ -235,27 +256,33 @@
       <td  style="background-color:#f3f3f3">
         <em>Small</em> screen
       </td>
-      <td style="font-size:.9em;">QVGA (240x320)</td>
+      <td style="font-size:.9em;"><strong>QVGA (240x320)</strong></td>
       </td>
       <td></td>
-      <td></td>
+      <td style="font-size:.9em;">480x640</td>
       <td></td>
     </tr>
     <tr>
       <td style="background-color:#f3f3f3">
         <em>Normal</em> screen
       </td>
-      <td style="font-size:.9em;">WQVGA400 (240x400)<br>WQVGA432 (240x432)</td>
-      <td style="font-size:.9em;">HVGA (320x480)</td>
-      <td style="font-size:.9em;">WVGA800 (480x800)<br>WVGA854 (480x854)</td>
-      <td style="font-size:.9em;"></td>
+      <td style="font-size:.9em;"><strong>WQVGA400 (240x400)</strong>
+        <br><strong>WQVGA432 (240x432)</strong></td>
+      <td style="font-size:.9em;"><strong>HVGA (320x480)</strong></td>
+      <td style="font-size:.9em;"><strong>WVGA800 (480x800)</strong>
+        <br><strong>WVGA854 (480x854)</strong>
+        <br>600x1024</td>
+      <td style="font-size:.9em;">640x960</td>
     </tr>
     <tr>
       <td style="background-color:#f3f3f3">
         <em>Large</em> screen
       </td>
-      <td></td>
-      <td style="font-size:.9em;">WVGA800* (480x800)<br>WVGA854* (480x854)</td>
+      <td style="font-size:.9em;"><strong>WVGA800** (480x800)</strong>
+        <br><strong>WVGA854** (480x854)</strong></td>
+      <td style="font-size:.9em;"><strong>WVGA800* (480x800)</strong>
+        <br><strong>WVGA854* (480x854)</strong>
+        <br>600x1024</td>
       <td></td>
       <td></td>
     </tr>
@@ -263,10 +290,13 @@
       <td style="background-color:#f3f3f3">
         <em>Extra Large</em> screen
       </td>
-      <td></td>
-      <td></td>
-      <td></td>
-      <td></td>
+      <td style="font-size:.9em;">600x1024</td>
+      <td style="font-size:.9em;">768x1024<br><strong>WXGA (768x1280)</strong>
+        <br>800x1280</td>
+      <td style="font-size:.9em;">1152x1536<br>1152x1920
+        <br>1200x1920</td>
+      <td style="font-size:.9em;">1536x2048<br>1536x2560
+        <br>1600x2560</td>
     </tr>
     <tr>
       <td colspan="4" style="border:none;font-size:90%;">* To emulate this
@@ -274,6 +304,12 @@
         creating an AVD that uses a WVGA800 or WVGA854 skin.
       </td>
     </tr>
+    <tr>
+      <td colspan="4" style="border:none;font-size:90%;">** To emulate this
+        configuration, specify a custom density of 120 when
+        creating an AVD that uses a WVGA800 or WVGA854 skin.
+      </td>
+    </tr>
 </table>
 
 <p>For an overview of the relative numbers of high (hdpi), medium (mdpi), and
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 1583dee..1da2622 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -337,21 +337,25 @@
         <li>{@code small}: Screens based on the space available on a
         low-density QVGA screen.  Considering a portrait HVGA display, this has
         the same available width but less height&mdash;it is 3:4 vs. HVGA's
-        2:3 aspect ratio.  Examples are QVGA low density and VGA high
+        2:3 aspect ratio.  The minimum layout size for this screen configuration
+        is approximately 320x426 dp units.  Examples are QVGA low density and VGA high
         density.</li>
         <li>{@code normal}: Screens based on the traditional
         medium-density HVGA screen.  A screen is considered to be normal if it is
-        at least this size (independent of density) and not larger.  Examples
+        at least this size (independent of density) and not larger.  The minimum
+        layout size for this screen configuration is approximately 320x470 dp units.  Examples
         of such screens a WQVGA low density, HVGA medium density, WVGA
         high density.</li>
         <li>{@code large}: Screens based on the space available on a
         medium-density VGA screen.  Such a screen has significantly more
         available space in both width and height than an HVGA display.
+        The minimum layout size for this screen configuration is approximately 480x640 dp units.
         Examples are VGA and WVGA medium density screens.</li>
         <li>{@code xlarge}: Screens that are considerably larger than the traditional
-        medium-density HVGA screen. In most cases, devices with extra large screens would be too
-large to carry in a pocket and would most likely be tablet-style devices. <em>Added in API Level
-9.</em></li>
+        medium-density HVGA screen. The minimum layout size for this screen configuration
+        is approximately 720x960 dp units.  In most cases, devices with extra large
+        screens would be too large to carry in a pocket and would most likely
+        be tablet-style devices. <em>Added in API Level 9.</em></li>
         </ul>
         <p><em>Added in API Level 4.</em></p>
         <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 96eb936..0949beb 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1534,6 +1534,48 @@
     }
 
     /**
+     * Convenience overload that takes a char array instead of a
+     * String.
+     *
+     * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+     * @hide
+     */
+    public float getTextRunAdvancesICU(char[] chars, int index, int count,
+            int contextIndex, int contextCount, int flags, float[] advances,
+            int advancesIndex) {
+
+        if ((index | count | contextIndex | contextCount | advancesIndex
+                | (index - contextIndex)
+                | ((contextIndex + contextCount) - (index + count))
+                | (chars.length - (contextIndex + contextCount))
+                | (advances == null ? 0 :
+                    (advances.length - (advancesIndex + count)))) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
+            throw new IllegalArgumentException("unknown flags value: " + flags);
+        }
+
+        if (!mHasCompatScaling) {
+            return native_getTextRunAdvancesICU(mNativePaint, chars, index, count,
+                    contextIndex, contextCount, flags, advances, advancesIndex);
+        }
+
+        final float oldSize = getTextSize();
+        setTextSize(oldSize * mCompatScaling);
+        float res = native_getTextRunAdvancesICU(mNativePaint, chars, index, count,
+                contextIndex, contextCount, flags, advances, advancesIndex);
+        setTextSize(oldSize);
+
+        if (advances != null) {
+            for (int i = advancesIndex, e = i + count; i < e; i++) {
+                advances[i] *= mInvCompatScaling;
+            }
+        }
+        return res * mInvCompatScaling; // assume errors are not significant
+    }
+
+    /**
      * Convenience overload that takes a CharSequence instead of a
      * String.
      *
@@ -1569,6 +1611,41 @@
     }
 
     /**
+     * Convenience overload that takes a CharSequence instead of a
+     * String.
+     *
+     * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+     * @hide
+     */
+    public float getTextRunAdvancesICU(CharSequence text, int start, int end,
+            int contextStart, int contextEnd, int flags, float[] advances,
+            int advancesIndex) {
+
+        if (text instanceof String) {
+            return getTextRunAdvancesICU((String) text, start, end,
+                    contextStart, contextEnd, flags, advances, advancesIndex);
+        }
+        if (text instanceof SpannedString ||
+            text instanceof SpannableString) {
+            return getTextRunAdvancesICU(text.toString(), start, end,
+                    contextStart, contextEnd, flags, advances, advancesIndex);
+        }
+        if (text instanceof GraphicsOperations) {
+            return ((GraphicsOperations) text).getTextRunAdvancesICU(start, end,
+                    contextStart, contextEnd, flags, advances, advancesIndex, this);
+        }
+
+        int contextLen = contextEnd - contextStart;
+        int len = end - start;
+        char[] buf = TemporaryBuffer.obtain(contextLen);
+        TextUtils.getChars(text, start, end, buf, 0);
+        float result = getTextRunAdvancesICU(buf, start - contextStart, len,
+                0, contextLen, flags, advances, advancesIndex);
+        TemporaryBuffer.recycle(buf);
+        return result;
+    }
+
+    /**
      * Returns the total advance width for the characters in the run
      * between start and end, and if advances is not null, the advance
      * assigned to each of these characters (java chars).
@@ -1644,6 +1721,44 @@
     }
 
     /**
+     * Temporary - DO NOT USE
+     *
+     * @hide
+     */
+    public float getTextRunAdvancesICU(String text, int start, int end, int contextStart,
+            int contextEnd, int flags, float[] advances, int advancesIndex) {
+
+        if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
+                | (start - contextStart) | (contextEnd - end)
+                | (text.length() - contextEnd)
+                | (advances == null ? 0 :
+                    (advances.length - advancesIndex - (end - start)))) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
+            throw new IllegalArgumentException("unknown flags value: " + flags);
+        }
+
+        if (!mHasCompatScaling) {
+            return native_getTextRunAdvancesICU(mNativePaint, text, start, end,
+                    contextStart, contextEnd, flags, advances, advancesIndex);
+        }
+
+        final float oldSize = getTextSize();
+        setTextSize(oldSize * mCompatScaling);
+        float totalAdvance = native_getTextRunAdvances(mNativePaint, text, start, end,
+                contextStart, contextEnd, flags, advances, advancesIndex);
+        setTextSize(oldSize);
+
+        if (advances != null) {
+            for (int i = advancesIndex, e = i + (end - start); i < e; i++) {
+                advances[i] *= mInvCompatScaling;
+            }
+        }
+        return totalAdvance * mInvCompatScaling; // assume errors are insignificant
+    }
+
+    /**
      * Returns the next cursor position in the run.  This avoids placing the
      * cursor between surrogates, between characters that form conjuncts,
      * between base characters and combining marks, or within a reordering
@@ -1907,6 +2022,13 @@
             String text, int start, int end, int contextStart, int contextEnd,
             int flags, float[] advances, int advancesIndex);
 
+    private static native float native_getTextRunAdvancesICU(int native_object,
+            char[] text, int index, int count, int contextIndex, int contextCount,
+            int flags, float[] advances, int advancesIndex);
+    private static native float native_getTextRunAdvancesICU(int native_object,
+            String text, int start, int end, int contextStart, int contextEnd,
+            int flags, float[] advances, int advancesIndex);
+
     private native int native_getTextRunCursor(int native_object, char[] text,
             int contextStart, int contextLength, int flags, int offset, int cursorOpt);
     private native int native_getTextRunCursor(int native_object, String text,
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 22fbdf9..a278466 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -67,7 +67,6 @@
     private final Rect mDstRect = new Rect();   // Gravity.apply() sets this
 
     private boolean mApplyGravity;
-    private boolean mRebuildShader;
     private boolean mMutated;
     
      // These are scaled to match the target density.
@@ -348,7 +347,7 @@
         if (state.mTileModeX != xmode || state.mTileModeY != ymode) {
             state.mTileModeX = xmode;
             state.mTileModeY = ymode;
-            mRebuildShader = true;
+            state.mRebuildShader = true;
             invalidateSelf();
         }
     }
@@ -369,7 +368,7 @@
         Bitmap bitmap = mBitmap;
         if (bitmap != null) {
             final BitmapState state = mBitmapState;
-            if (mRebuildShader) {
+            if (state.mRebuildShader) {
                 Shader.TileMode tmx = state.mTileModeX;
                 Shader.TileMode tmy = state.mTileModeY;
 
@@ -380,7 +379,7 @@
                             tmx == null ? Shader.TileMode.CLAMP : tmx,
                             tmy == null ? Shader.TileMode.CLAMP : tmy));
                 }
-                mRebuildShader = false;
+                state.mRebuildShader = false;
                 copyBounds(mDstRect);
             }
 
@@ -424,7 +423,6 @@
     public Drawable mutate() {
         if (!mMutated && super.mutate() == this) {
             mBitmapState = new BitmapState(mBitmapState);
-            mRebuildShader = true;
             mMutated = true;
         }
         return this;
@@ -511,6 +509,7 @@
         Shader.TileMode mTileModeX = null;
         Shader.TileMode mTileModeY = null;
         int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
+        boolean mRebuildShader;
 
         BitmapState(Bitmap bitmap) {
             mBitmap = bitmap;
@@ -524,18 +523,19 @@
             mTileModeY = bitmapState.mTileModeY;
             mTargetDensity = bitmapState.mTargetDensity;
             mPaint = new Paint(bitmapState.mPaint);
+            mRebuildShader = bitmapState.mRebuildShader;
         }
 
         @Override
         public Drawable newDrawable() {
             return new BitmapDrawable(this, null);
         }
-        
+
         @Override
         public Drawable newDrawable(Resources res) {
             return new BitmapDrawable(this, res);
         }
-        
+
         @Override
         public int getChangingConfigurations() {
             return mChangingConfigurations;
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index d30e908..6b6fcdf 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -84,6 +84,8 @@
         return String8();
     }
 
+    virtual String8 getMIMEType() const;
+
 protected:
     virtual ~DataSource() {}
 
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 66dfff6..6a21627 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -45,6 +45,7 @@
 extern const char *MEDIA_MIMETYPE_CONTAINER_OGG;
 extern const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA;
 extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS;
+extern const char *MEDIA_MIMETYPE_CONTAINER_AVI;
 
 extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
 
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 8e8b61b..b22986d 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -128,6 +128,12 @@
     // input device or an application with system-wide event injection permission.
     POLICY_FLAG_TRUSTED = 0x02000000,
 
+    // Indicates that the input event has passed through an input filter.
+    POLICY_FLAG_FILTERED = 0x04000000,
+
+    // Disables automatic key repeating behavior.
+    POLICY_FLAG_DISABLE_KEY_REPEAT = 0x08000000,
+
     /* These flags are set by the input reader policy as it intercepts each event. */
 
     // Indicates that the screen was off when the event was received and the event
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index 972e799..0a6e4fb 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -291,6 +291,9 @@
     void* reserved_proc[2];
 };
 
+// Backwards compatibility...  please switch to ANativeWindow.
+typedef struct ANativeWindow android_native_window_t;
+
 /*
  *  native_window_set_usage(..., usage)
  *  Sets the intended usage flags for the next buffers
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index c41e0ad..352579a 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -66,8 +66,9 @@
     public static final int FILE_TYPE_ASF     = 26;
     public static final int FILE_TYPE_MKV     = 27;
     public static final int FILE_TYPE_MP2TS   = 28;
+    public static final int FILE_TYPE_AVI     = 29;
     private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4;
-    private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_MP2TS;
+    private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_AVI;
     
     // Image file types
     public static final int FILE_TYPE_JPEG    = 31;
@@ -198,6 +199,7 @@
         addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska");
         addFileType("WEBM", FILE_TYPE_MKV, "video/x-matroska");
         addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts");
+        addFileType("AVI", FILE_TYPE_AVI, "video/avi");
 
         if (isWMVEnabled()) {
             addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv", MtpConstants.FORMAT_WMV);
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 7fdf448..7c181ee 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -83,7 +83,7 @@
      *
      * @param filePath the path of image file
      * @param kind could be MINI_KIND or MICRO_KIND
-     * @return Bitmap
+     * @return Bitmap, or null on failures
      *
      * @hide This method is only used by media framework and media provider internally.
      */
@@ -123,6 +123,8 @@
                 bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options);
             } catch (IOException ex) {
                 Log.e(TAG, "", ex);
+            } catch (OutOfMemoryError oom) {
+                Log.e(TAG, "Unable to decode file " + filePath + ". OutOfMemoryError.", oom);
             }
         }
 
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 346d0bb..9928f44 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1081,8 +1081,8 @@
                 android_native_rect_t crop;
                 crop.left = rect.nLeft;
                 crop.top = rect.nTop;
-                crop.right = rect.nLeft + rect.nWidth - 1;
-                crop.bottom = rect.nTop + rect.nHeight - 1;
+                crop.right = rect.nLeft + rect.nWidth;
+                crop.bottom = rect.nTop + rect.nHeight;
 
                 CHECK_EQ(0, native_window_set_crop(
                             mNativeWindow.get(), &crop));
diff --git a/media/libstagefright/AVIExtractor.cpp b/media/libstagefright/AVIExtractor.cpp
new file mode 100644
index 0000000..6313ca3
--- /dev/null
+++ b/media/libstagefright/AVIExtractor.cpp
@@ -0,0 +1,922 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AVIExtractor"
+#include <utils/Log.h>
+
+#include "include/AVIExtractor.h"
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+struct AVIExtractor::AVISource : public MediaSource {
+    AVISource(const sp<AVIExtractor> &extractor, size_t trackIndex);
+
+    virtual status_t start(MetaData *params);
+    virtual status_t stop();
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options);
+
+protected:
+    virtual ~AVISource();
+
+private:
+    sp<AVIExtractor> mExtractor;
+    size_t mTrackIndex;
+    const AVIExtractor::Track &mTrack;
+    MediaBufferGroup *mBufferGroup;
+    size_t mSampleIndex;
+
+    DISALLOW_EVIL_CONSTRUCTORS(AVISource);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+AVIExtractor::AVISource::AVISource(
+        const sp<AVIExtractor> &extractor, size_t trackIndex)
+    : mExtractor(extractor),
+      mTrackIndex(trackIndex),
+      mTrack(mExtractor->mTracks.itemAt(trackIndex)),
+      mBufferGroup(NULL) {
+}
+
+AVIExtractor::AVISource::~AVISource() {
+    if (mBufferGroup) {
+        stop();
+    }
+}
+
+status_t AVIExtractor::AVISource::start(MetaData *params) {
+    CHECK(!mBufferGroup);
+
+    mBufferGroup = new MediaBufferGroup;
+
+    mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize));
+    mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize));
+    mSampleIndex = 0;
+
+    return OK;
+}
+
+status_t AVIExtractor::AVISource::stop() {
+    CHECK(mBufferGroup);
+
+    delete mBufferGroup;
+    mBufferGroup = NULL;
+
+    return OK;
+}
+
+sp<MetaData> AVIExtractor::AVISource::getFormat() {
+    return mTrack.mMeta;
+}
+
+status_t AVIExtractor::AVISource::read(
+        MediaBuffer **buffer, const ReadOptions *options) {
+    CHECK(mBufferGroup);
+
+    *buffer = NULL;
+
+    int64_t seekTimeUs;
+    ReadOptions::SeekMode seekMode;
+    if (options && options->getSeekTo(&seekTimeUs, &seekMode)) {
+        status_t err =
+            mExtractor->getSampleIndexAtTime(
+                    mTrackIndex, seekTimeUs, seekMode, &mSampleIndex);
+
+        if (err != OK) {
+            return ERROR_END_OF_STREAM;
+        }
+    }
+
+    int64_t timeUs =
+        (mSampleIndex * 1000000ll * mTrack.mRate) / mTrack.mScale;
+
+    off64_t offset;
+    size_t size;
+    bool isKey;
+    status_t err = mExtractor->getSampleInfo(
+            mTrackIndex, mSampleIndex, &offset, &size, &isKey);
+
+    ++mSampleIndex;
+
+    if (err != OK) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    MediaBuffer *out;
+    CHECK_EQ(mBufferGroup->acquire_buffer(&out), (status_t)OK);
+
+    ssize_t n = mExtractor->mDataSource->readAt(offset, out->data(), size);
+
+    if (n < (ssize_t)size) {
+        return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED;
+    }
+
+    out->set_range(0, size);
+
+    out->meta_data()->setInt64(kKeyTime, timeUs);
+
+    if (isKey) {
+        out->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+    }
+
+    *buffer = out;
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+AVIExtractor::AVIExtractor(const sp<DataSource> &dataSource)
+    : mDataSource(dataSource) {
+    mInitCheck = parseHeaders();
+
+    if (mInitCheck != OK) {
+        mTracks.clear();
+    }
+}
+
+AVIExtractor::~AVIExtractor() {
+}
+
+size_t AVIExtractor::countTracks() {
+    return mTracks.size();
+}
+
+sp<MediaSource> AVIExtractor::getTrack(size_t index) {
+    return index < mTracks.size() ? new AVISource(this, index) : NULL;
+}
+
+sp<MetaData> AVIExtractor::getTrackMetaData(
+        size_t index, uint32_t flags) {
+    return index < mTracks.size() ? mTracks.editItemAt(index).mMeta : NULL;
+}
+
+sp<MetaData> AVIExtractor::getMetaData() {
+    sp<MetaData> meta = new MetaData;
+
+    if (mInitCheck == OK) {
+        meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_AVI);
+    }
+
+    return meta;
+}
+
+status_t AVIExtractor::parseHeaders() {
+    mTracks.clear();
+    mMovieOffset = 0;
+    mFoundIndex = false;
+    mOffsetsAreAbsolute = false;
+
+    ssize_t res = parseChunk(0ll, -1ll);
+
+    if (res < 0) {
+        return (status_t)res;
+    }
+
+    if (mMovieOffset == 0ll || !mFoundIndex) {
+        return ERROR_MALFORMED;
+    }
+
+    return OK;
+}
+
+ssize_t AVIExtractor::parseChunk(off64_t offset, off64_t size, int depth) {
+    if (size >= 0 && size < 8) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t tmp[12];
+    ssize_t n = mDataSource->readAt(offset, tmp, 8);
+
+    if (n < 8) {
+        return (n < 0) ? n : (ssize_t)ERROR_MALFORMED;
+    }
+
+    uint32_t fourcc = U32_AT(tmp);
+    uint32_t chunkSize = U32LE_AT(&tmp[4]);
+
+    if (size >= 0 && chunkSize + 8 > size) {
+        return ERROR_MALFORMED;
+    }
+
+    static const char kPrefix[] = "                              ";
+    const char *prefix = &kPrefix[strlen(kPrefix) - 2 * depth];
+
+    if (fourcc == FOURCC('L', 'I', 'S', 'T')
+            || fourcc == FOURCC('R', 'I', 'F', 'F')) {
+        // It's a list of chunks
+
+        if (size >= 0 && size < 12) {
+            return ERROR_MALFORMED;
+        }
+
+        n = mDataSource->readAt(offset + 8, &tmp[8], 4);
+
+        if (n < 4) {
+            return (n < 0) ? n : (ssize_t)ERROR_MALFORMED;
+        }
+
+        uint32_t subFourcc = U32_AT(&tmp[8]);
+
+        LOGV("%s offset 0x%08llx LIST of '%c%c%c%c', size %d",
+             prefix,
+             offset,
+             (char)(subFourcc >> 24),
+             (char)((subFourcc >> 16) & 0xff),
+             (char)((subFourcc >> 8) & 0xff),
+             (char)(subFourcc & 0xff),
+             chunkSize - 4);
+
+        if (subFourcc == FOURCC('m', 'o', 'v', 'i')) {
+            // We're not going to parse this, but will take note of the
+            // offset.
+
+            mMovieOffset = offset;
+        } else {
+            off64_t subOffset = offset + 12;
+            off64_t subOffsetLimit = subOffset + chunkSize - 4;
+            while (subOffset < subOffsetLimit) {
+                ssize_t res =
+                    parseChunk(subOffset, subOffsetLimit - subOffset, depth + 1);
+
+                if (res < 0) {
+                    return res;
+                }
+
+                subOffset += res;
+            }
+        }
+    } else {
+        LOGV("%s offset 0x%08llx CHUNK '%c%c%c%c'",
+             prefix,
+             offset,
+             (char)(fourcc >> 24),
+             (char)((fourcc >> 16) & 0xff),
+             (char)((fourcc >> 8) & 0xff),
+             (char)(fourcc & 0xff));
+
+        status_t err = OK;
+
+        switch (fourcc) {
+            case FOURCC('s', 't', 'r', 'h'):
+            {
+                err = parseStreamHeader(offset + 8, chunkSize);
+                break;
+            }
+
+            case FOURCC('s', 't', 'r', 'f'):
+            {
+                err = parseStreamFormat(offset + 8, chunkSize);
+                break;
+            }
+
+            case FOURCC('i', 'd', 'x', '1'):
+            {
+                err = parseIndex(offset + 8, chunkSize);
+                break;
+            }
+
+            default:
+                break;
+        }
+
+        if (err != OK) {
+            return err;
+        }
+    }
+
+    if (chunkSize & 1) {
+        ++chunkSize;
+    }
+
+    return chunkSize + 8;
+}
+
+static const char *GetMIMETypeForHandler(uint32_t handler) {
+    switch (handler) {
+        // Wow... shamelessly copied from
+        // http://wiki.multimedia.cx/index.php?title=ISO_MPEG-4
+
+        case FOURCC('3', 'I', 'V', '2'):
+        case FOURCC('3', 'i', 'v', '2'):
+        case FOURCC('B', 'L', 'Z', '0'):
+        case FOURCC('D', 'I', 'G', 'I'):
+        case FOURCC('D', 'I', 'V', '1'):
+        case FOURCC('d', 'i', 'v', '1'):
+        case FOURCC('D', 'I', 'V', 'X'):
+        case FOURCC('d', 'i', 'v', 'x'):
+        case FOURCC('D', 'X', '5', '0'):
+        case FOURCC('d', 'x', '5', '0'):
+        case FOURCC('D', 'X', 'G', 'M'):
+        case FOURCC('E', 'M', '4', 'A'):
+        case FOURCC('E', 'P', 'H', 'V'):
+        case FOURCC('F', 'M', 'P', '4'):
+        case FOURCC('f', 'm', 'p', '4'):
+        case FOURCC('F', 'V', 'F', 'W'):
+        case FOURCC('H', 'D', 'X', '4'):
+        case FOURCC('h', 'd', 'x', '4'):
+        case FOURCC('M', '4', 'C', 'C'):
+        case FOURCC('M', '4', 'S', '2'):
+        case FOURCC('m', '4', 's', '2'):
+        case FOURCC('M', 'P', '4', 'S'):
+        case FOURCC('m', 'p', '4', 's'):
+        case FOURCC('M', 'P', '4', 'V'):
+        case FOURCC('m', 'p', '4', 'v'):
+        case FOURCC('M', 'V', 'X', 'M'):
+        case FOURCC('R', 'M', 'P', '4'):
+        case FOURCC('S', 'E', 'D', 'G'):
+        case FOURCC('S', 'M', 'P', '4'):
+        case FOURCC('U', 'M', 'P', '4'):
+        case FOURCC('W', 'V', '1', 'F'):
+        case FOURCC('X', 'V', 'I', 'D'):
+        case FOURCC('X', 'v', 'i', 'D'):
+        case FOURCC('x', 'v', 'i', 'd'):
+        case FOURCC('X', 'V', 'I', 'X'):
+            return MEDIA_MIMETYPE_VIDEO_MPEG4;
+
+        default:
+            return NULL;
+    }
+}
+
+status_t AVIExtractor::parseStreamHeader(off64_t offset, size_t size) {
+    if (size != 56) {
+        return ERROR_MALFORMED;
+    }
+
+    if (mTracks.size() > 99) {
+        return -ERANGE;
+    }
+
+    sp<ABuffer> buffer = new ABuffer(size);
+    ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size());
+
+    if (n < (ssize_t)size) {
+        return n < 0 ? (status_t)n : ERROR_MALFORMED;
+    }
+
+    const uint8_t *data = buffer->data();
+
+    uint32_t type = U32_AT(data);
+    uint32_t handler = U32_AT(&data[4]);
+    uint32_t flags = U32LE_AT(&data[8]);
+
+    sp<MetaData> meta = new MetaData;
+
+    uint32_t rate = U32LE_AT(&data[20]);
+    uint32_t scale = U32LE_AT(&data[24]);
+
+    const char *mime = NULL;
+    Track::Kind kind = Track::OTHER;
+
+    if (type == FOURCC('v', 'i', 'd', 's')) {
+        mime = GetMIMETypeForHandler(handler);
+
+        if (mime && strncasecmp(mime, "video/", 6)) {
+            return ERROR_MALFORMED;
+        }
+
+        kind = Track::VIDEO;
+    } else if (type == FOURCC('a', 'u', 'd', 's')) {
+        if (mime && strncasecmp(mime, "audio/", 6)) {
+            return ERROR_MALFORMED;
+        }
+
+        kind = Track::AUDIO;
+    }
+
+    if (!mime) {
+        mime = "application/octet-stream";
+    }
+
+    meta->setCString(kKeyMIMEType, mime);
+
+    mTracks.push();
+    Track *track = &mTracks.editItemAt(mTracks.size() - 1);
+
+    track->mMeta = meta;
+    track->mRate = rate;
+    track->mScale = scale;
+    track->mKind = kind;
+    track->mNumSyncSamples = 0;
+    track->mThumbnailSampleSize = 0;
+    track->mThumbnailSampleIndex = -1;
+    track->mMaxSampleSize = 0;
+
+    return OK;
+}
+
+status_t AVIExtractor::parseStreamFormat(off64_t offset, size_t size) {
+    if (mTracks.isEmpty()) {
+        return ERROR_MALFORMED;
+    }
+
+    Track *track = &mTracks.editItemAt(mTracks.size() - 1);
+
+    if (track->mKind == Track::OTHER) {
+        // We don't support this content, but that's not a parsing error.
+        return OK;
+    }
+
+    bool isVideo = (track->mKind == Track::VIDEO);
+
+    if ((isVideo && size < 40) || (!isVideo && size < 18)) {
+        // Expected a BITMAPINFO or WAVEFORMATEX structure, respectively.
+        return ERROR_MALFORMED;
+    }
+
+    sp<ABuffer> buffer = new ABuffer(size);
+    ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size());
+
+    if (n < (ssize_t)size) {
+        return n < 0 ? (status_t)n : ERROR_MALFORMED;
+    }
+
+    const uint8_t *data = buffer->data();
+
+    if (isVideo) {
+        uint32_t width = U32LE_AT(&data[4]);
+        uint32_t height = U32LE_AT(&data[8]);
+
+        track->mMeta->setInt32(kKeyWidth, width);
+        track->mMeta->setInt32(kKeyHeight, height);
+    } else {
+        uint32_t format = U16LE_AT(data);
+        if (format == 0x55) {
+            track->mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+        }
+
+        uint32_t numChannels = U16LE_AT(&data[2]);
+        uint32_t sampleRate = U32LE_AT(&data[4]);
+
+        track->mMeta->setInt32(kKeyChannelCount, numChannels);
+        track->mMeta->setInt32(kKeySampleRate, sampleRate);
+    }
+
+    return OK;
+}
+
+// static
+bool AVIExtractor::IsCorrectChunkType(
+        ssize_t trackIndex, Track::Kind kind, uint32_t chunkType) {
+    uint32_t chunkBase = chunkType & 0xffff;
+
+    switch (kind) {
+        case Track::VIDEO:
+        {
+            if (chunkBase != FOURCC(0, 0, 'd', 'c')
+                    && chunkBase != FOURCC(0, 0, 'd', 'b')) {
+                return false;
+            }
+            break;
+        }
+
+        case Track::AUDIO:
+        {
+            if (chunkBase != FOURCC(0, 0, 'w', 'b')) {
+                return false;
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    if (trackIndex < 0) {
+        return true;
+    }
+
+    uint8_t hi = chunkType >> 24;
+    uint8_t lo = (chunkType >> 16) & 0xff;
+
+    if (hi < '0' || hi > '9' || lo < '0' || lo > '9') {
+        return false;
+    }
+
+    if (trackIndex != (10 * (hi - '0') + (lo - '0'))) {
+        return false;
+    }
+
+    return true;
+}
+
+status_t AVIExtractor::parseIndex(off64_t offset, size_t size) {
+    if ((size % 16) != 0) {
+        return ERROR_MALFORMED;
+    }
+
+    sp<ABuffer> buffer = new ABuffer(size);
+    ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size());
+
+    if (n < (ssize_t)size) {
+        return n < 0 ? (status_t)n : ERROR_MALFORMED;
+    }
+
+    const uint8_t *data = buffer->data();
+
+    while (size > 0) {
+        uint32_t chunkType = U32_AT(data);
+
+        uint8_t hi = chunkType >> 24;
+        uint8_t lo = (chunkType >> 16) & 0xff;
+
+        if (hi < '0' || hi > '9' || lo < '0' || lo > '9') {
+            return ERROR_MALFORMED;
+        }
+
+        size_t trackIndex = 10 * (hi - '0') + (lo - '0');
+
+        if (trackIndex >= mTracks.size()) {
+            return ERROR_MALFORMED;
+        }
+
+        Track *track = &mTracks.editItemAt(trackIndex);
+
+        if (!IsCorrectChunkType(-1, track->mKind, chunkType)) {
+            return ERROR_MALFORMED;
+        }
+
+        if (track->mKind == Track::OTHER) {
+            data += 16;
+            size -= 16;
+            continue;
+        }
+
+        uint32_t flags = U32LE_AT(&data[4]);
+        uint32_t offset = U32LE_AT(&data[8]);
+        uint32_t chunkSize = U32LE_AT(&data[12]);
+
+        if (chunkSize > track->mMaxSampleSize) {
+            track->mMaxSampleSize = chunkSize;
+        }
+
+        track->mSamples.push();
+
+        SampleInfo *info =
+            &track->mSamples.editItemAt(track->mSamples.size() - 1);
+
+        info->mOffset = offset;
+        info->mIsKey = (flags & 0x10) != 0;
+
+        if (info->mIsKey) {
+            static const size_t kMaxNumSyncSamplesToScan = 20;
+
+            if (track->mNumSyncSamples < kMaxNumSyncSamplesToScan) {
+                if (chunkSize > track->mThumbnailSampleSize) {
+                    track->mThumbnailSampleSize = chunkSize;
+
+                    track->mThumbnailSampleIndex =
+                        track->mSamples.size() - 1;
+                }
+            }
+
+            ++track->mNumSyncSamples;
+        }
+
+        data += 16;
+        size -= 16;
+    }
+
+    if (!mTracks.isEmpty()) {
+        off64_t offset;
+        size_t size;
+        bool isKey;
+        status_t err = getSampleInfo(0, 0, &offset, &size, &isKey);
+
+        if (err != OK) {
+            mOffsetsAreAbsolute = !mOffsetsAreAbsolute;
+            err = getSampleInfo(0, 0, &offset, &size, &isKey);
+
+            if (err != OK) {
+                return err;
+            }
+        }
+
+        LOGV("Chunk offsets are %s",
+             mOffsetsAreAbsolute ? "absolute" : "movie-chunk relative");
+    }
+
+    for (size_t i = 0; i < mTracks.size(); ++i) {
+        Track *track = &mTracks.editItemAt(i);
+
+        int64_t durationUs =
+            (track->mSamples.size() * 1000000ll * track->mRate) / track->mScale;
+
+        LOGV("track %d duration = %.2f secs", i, durationUs / 1E6);
+
+        track->mMeta->setInt64(kKeyDuration, durationUs);
+        track->mMeta->setInt32(kKeyMaxInputSize, track->mMaxSampleSize);
+
+        const char *tmp;
+        CHECK(track->mMeta->findCString(kKeyMIMEType, &tmp));
+
+        AString mime = tmp;
+
+        if (!strncasecmp("video/", mime.c_str(), 6)
+                && track->mThumbnailSampleIndex >= 0) {
+            int64_t thumbnailTimeUs =
+                (track->mThumbnailSampleIndex * 1000000ll * track->mRate)
+                    / track->mScale;
+
+            track->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs);
+
+            if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG4)) {
+                status_t err = addMPEG4CodecSpecificData(i);
+
+                if (err != OK) {
+                    return err;
+                }
+            }
+        }
+    }
+
+    mFoundIndex = true;
+
+    return OK;
+}
+
+static size_t GetSizeWidth(size_t x) {
+    size_t n = 1;
+    while (x > 127) {
+        ++n;
+        x >>= 7;
+    }
+    return n;
+}
+
+static uint8_t *EncodeSize(uint8_t *dst, size_t x) {
+    while (x > 127) {
+        *dst++ = (x & 0x7f) | 0x80;
+        x >>= 7;
+    }
+    *dst++ = x;
+    return dst;
+}
+
+sp<ABuffer> MakeMPEG4VideoCodecSpecificData(const sp<ABuffer> &config) {
+    size_t len1 = config->size() + GetSizeWidth(config->size()) + 1;
+    size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13;
+    size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3;
+
+    sp<ABuffer> csd = new ABuffer(len3);
+    uint8_t *dst = csd->data();
+    *dst++ = 0x03;
+    dst = EncodeSize(dst, len2 + 3);
+    *dst++ = 0x00;  // ES_ID
+    *dst++ = 0x00;
+    *dst++ = 0x00;  // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+    *dst++ = 0x04;
+    dst = EncodeSize(dst, len1 + 13);
+    *dst++ = 0x01;  // Video ISO/IEC 14496-2 Simple Profile
+    for (size_t i = 0; i < 12; ++i) {
+        *dst++ = 0x00;
+    }
+
+    *dst++ = 0x05;
+    dst = EncodeSize(dst, config->size());
+    memcpy(dst, config->data(), config->size());
+    dst += config->size();
+
+    // hexdump(csd->data(), csd->size());
+
+    return csd;
+}
+
+status_t AVIExtractor::addMPEG4CodecSpecificData(size_t trackIndex) {
+    Track *track = &mTracks.editItemAt(trackIndex);
+
+    off64_t offset;
+    size_t size;
+    bool isKey;
+    status_t err = getSampleInfo(trackIndex, 0, &offset, &size, &isKey);
+
+    if (err != OK) {
+        return err;
+    }
+
+    sp<ABuffer> buffer = new ABuffer(size);
+    ssize_t n = mDataSource->readAt(offset, buffer->data(), buffer->size());
+
+    if (n < (ssize_t)size) {
+        return n < 0 ? (status_t)n : ERROR_MALFORMED;
+    }
+
+    // Extract everything up to the first VOP start code from the first
+    // frame's encoded data and use it to construct an ESDS with the
+    // codec specific data.
+
+    size_t i = 0;
+    bool found = false;
+    while (i + 3 < buffer->size()) {
+        if (!memcmp("\x00\x00\x01\xb6", &buffer->data()[i], 4)) {
+            found = true;
+            break;
+        }
+
+        ++i;
+    }
+
+    if (!found) {
+        return ERROR_MALFORMED;
+    }
+
+    buffer->setRange(0, i);
+
+    sp<ABuffer> csd = MakeMPEG4VideoCodecSpecificData(buffer);
+    track->mMeta->setData(kKeyESDS, kTypeESDS, csd->data(), csd->size());
+
+    return OK;
+}
+
+status_t AVIExtractor::getSampleInfo(
+        size_t trackIndex, size_t sampleIndex,
+        off64_t *offset, size_t *size, bool *isKey) {
+    if (trackIndex >= mTracks.size()) {
+        return -ERANGE;
+    }
+
+    const Track &track = mTracks.itemAt(trackIndex);
+
+    if (sampleIndex >= track.mSamples.size()) {
+        return -ERANGE;
+    }
+
+    const SampleInfo &info = track.mSamples.itemAt(sampleIndex);
+
+    if (!mOffsetsAreAbsolute) {
+        *offset = info.mOffset + mMovieOffset + 8;
+    } else {
+        *offset = info.mOffset;
+    }
+
+    *size = 0;
+
+    uint8_t tmp[8];
+    ssize_t n = mDataSource->readAt(*offset, tmp, 8);
+
+    if (n < 8) {
+        return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED;
+    }
+
+    uint32_t chunkType = U32_AT(tmp);
+
+    if (!IsCorrectChunkType(trackIndex, track.mKind, chunkType)) {
+        return ERROR_MALFORMED;
+    }
+
+    *offset += 8;
+    *size = U32LE_AT(&tmp[4]);
+
+    *isKey = info.mIsKey;
+
+    return OK;
+}
+
+status_t AVIExtractor::getSampleIndexAtTime(
+        size_t trackIndex,
+        int64_t timeUs, MediaSource::ReadOptions::SeekMode mode,
+        size_t *sampleIndex) const {
+    if (trackIndex >= mTracks.size()) {
+        return -ERANGE;
+    }
+
+    const Track &track = mTracks.itemAt(trackIndex);
+
+    ssize_t closestSampleIndex =
+        timeUs / track.mRate * track.mScale / 1000000ll;
+
+    ssize_t numSamples = track.mSamples.size();
+
+    if (closestSampleIndex < 0) {
+        closestSampleIndex = 0;
+    } else if (closestSampleIndex >= numSamples) {
+        closestSampleIndex = numSamples - 1;
+    }
+
+    if (mode == MediaSource::ReadOptions::SEEK_CLOSEST) {
+        *sampleIndex = closestSampleIndex;
+
+        return OK;
+    }
+
+    ssize_t prevSyncSampleIndex = closestSampleIndex;
+    while (prevSyncSampleIndex >= 0) {
+        const SampleInfo &info =
+            track.mSamples.itemAt(prevSyncSampleIndex);
+
+        if (info.mIsKey) {
+            break;
+        }
+
+        --prevSyncSampleIndex;
+    }
+
+    ssize_t nextSyncSampleIndex = closestSampleIndex;
+    while (nextSyncSampleIndex < numSamples) {
+        const SampleInfo &info =
+            track.mSamples.itemAt(nextSyncSampleIndex);
+
+        if (info.mIsKey) {
+            break;
+        }
+
+        ++nextSyncSampleIndex;
+    }
+
+    switch (mode) {
+        case MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC:
+        {
+            *sampleIndex = prevSyncSampleIndex;
+
+            return prevSyncSampleIndex >= 0 ? OK : UNKNOWN_ERROR;
+        }
+
+        case MediaSource::ReadOptions::SEEK_NEXT_SYNC:
+        {
+            *sampleIndex = nextSyncSampleIndex;
+
+            return nextSyncSampleIndex < numSamples ? OK : UNKNOWN_ERROR;
+        }
+
+        case MediaSource::ReadOptions::SEEK_CLOSEST_SYNC:
+        {
+            if (prevSyncSampleIndex < 0 && nextSyncSampleIndex >= numSamples) {
+                return UNKNOWN_ERROR;
+            }
+
+            if (prevSyncSampleIndex < 0) {
+                *sampleIndex = nextSyncSampleIndex;
+                return OK;
+            }
+
+            if (nextSyncSampleIndex >= numSamples) {
+                *sampleIndex = prevSyncSampleIndex;
+                return OK;
+            }
+
+            size_t dist1 = closestSampleIndex - prevSyncSampleIndex;
+            size_t dist2 = nextSyncSampleIndex - closestSampleIndex;
+
+            *sampleIndex =
+                (dist1 < dist2) ? prevSyncSampleIndex : nextSyncSampleIndex;
+
+            return OK;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+bool SniffAVI(
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *) {
+    char tmp[12];
+    if (source->readAt(0, tmp, 12) < 12) {
+        return false;
+    }
+
+    if (!memcmp(tmp, "RIFF", 4) && !memcmp(&tmp[8], "AVI ", 4)) {
+        mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_AVI);
+        *confidence = 0.2;
+
+        return true;
+    }
+
+    return false;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index d2e8b46..2f3e1410 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -8,6 +8,7 @@
         AACExtractor.cpp                  \
         AMRExtractor.cpp                  \
         AMRWriter.cpp                     \
+        AVIExtractor.cpp                  \
         AudioPlayer.cpp                   \
         AudioSource.cpp                   \
         AwesomePlayer.cpp                 \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 7940de0..6b0b1b9 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define DEBUG_HDCP
+#undef DEBUG_HDCP
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AwesomePlayer"
@@ -1693,29 +1693,37 @@
 
         dataSource = mCachedSource;
 
-        // We're going to prefill the cache before trying to instantiate
-        // the extractor below, as the latter is an operation that otherwise
-        // could block on the datasource for a significant amount of time.
-        // During that time we'd be unable to abort the preparation phase
-        // without this prefill.
+        String8 contentType = dataSource->getMIMEType();
 
-        mLock.unlock();
+        if (strncasecmp(contentType.string(), "audio/", 6)) {
+            // We're not doing this for streams that appear to be audio-only
+            // streams to ensure that even low bandwidth streams start
+            // playing back fairly instantly.
 
-        for (;;) {
-            status_t finalStatus;
-            size_t cachedDataRemaining =
-                mCachedSource->approxDataRemaining(&finalStatus);
+            // We're going to prefill the cache before trying to instantiate
+            // the extractor below, as the latter is an operation that otherwise
+            // could block on the datasource for a significant amount of time.
+            // During that time we'd be unable to abort the preparation phase
+            // without this prefill.
 
-            if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes
-                    || (mFlags & PREPARE_CANCELLED)) {
-                break;
+            mLock.unlock();
+
+            for (;;) {
+                status_t finalStatus;
+                size_t cachedDataRemaining =
+                    mCachedSource->approxDataRemaining(&finalStatus);
+
+                if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes
+                        || (mFlags & PREPARE_CANCELLED)) {
+                    break;
+                }
+
+                usleep(200000);
             }
 
-            usleep(200000);
+            mLock.lock();
         }
 
-        mLock.lock();
-
         if (mFlags & PREPARE_CANCELLED) {
             LOGI("Prepare cancelled while waiting for initial cache fill.");
             return UNKNOWN_ERROR;
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index b5c51f4..c16b3b5 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "include/AMRExtractor.h"
+#include "include/AVIExtractor.h"
 #include "include/MP3Extractor.h"
 #include "include/MPEG4Extractor.h"
 #include "include/WAVExtractor.h"
@@ -111,6 +112,7 @@
     RegisterSniffer(SniffMPEG2TS);
     RegisterSniffer(SniffMP3);
     RegisterSniffer(SniffAAC);
+    RegisterSniffer(SniffAVI);
 
     char value[PROPERTY_VALUE_MAX];
     if (property_get("drm.service.enabled", value, NULL)
@@ -144,4 +146,8 @@
     return source;
 }
 
+String8 DataSource::getMIMEType() const {
+    return String8("application/octet-stream");
+}
+
 }  // namespace android
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 0be7261..8ca6ee8 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -43,6 +43,7 @@
 const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
 const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
 const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts";
+const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi";
 
 const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
 
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 23bad5b..af0131e 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 
 #include "include/AMRExtractor.h"
+#include "include/AVIExtractor.h"
 #include "include/MP3Extractor.h"
 #include "include/MPEG4Extractor.h"
 #include "include/WAVExtractor.h"
@@ -108,6 +109,8 @@
         ret = new MatroskaExtractor(source);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
         ret = new MPEG2TSExtractor(source);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AVI)) {
+        ret = new AVIExtractor(source);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) {
         ret = new WVMExtractor(source);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 3c99d1c..c1aa46e 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -493,4 +493,8 @@
     return mSource->getUri();
 }
 
+String8 NuCachedSource2::getMIMEType() const {
+    return mSource->getMIMEType();
+}
+
 }  // namespace android
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 73daf12..821ba9b 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -136,6 +136,7 @@
     unsigned port;
 
     mUri = uri;
+    mContentType = String8("application/octet-stream");
 
     bool https;
     if (!ParseURL(uri, &host, &port, &path, &https)) {
@@ -265,6 +266,15 @@
             }
         }
 
+        {
+            AString value;
+            if (mHTTP.find_header_value("Content-Type", &value)) {
+                mContentType = String8(value.c_str());
+            } else {
+                mContentType = String8("application/octet-stream");
+            }
+        }
+
         applyTimeoutResponse();
 
         if (offset == 0) {
@@ -410,7 +420,14 @@
             internalRead((uint8_t *)data + numBytesRead, size - numBytesRead);
 
         if (n < 0) {
-            return n;
+            if (numBytesRead == 0 || mContentLengthValid) {
+                return n;
+            }
+
+            // If there was an error we want to at least return the data
+            // we've already successfully read. The next call to read will
+            // then return the error.
+            n = 0;
         }
 
         int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
@@ -564,4 +581,8 @@
     return mUri;
 }
 
+String8 NuHTTPDataSource::getMIMEType() const {
+    return mContentType;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 904bd62..c278992 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -2240,8 +2240,8 @@
                         android_native_rect_t crop;
                         crop.left = left;
                         crop.top = top;
-                        crop.right = right;
-                        crop.bottom = bottom;
+                        crop.right = right + 1;
+                        crop.bottom = bottom + 1;
 
                         // We'll ignore any errors here, if the surface is
                         // already invalid, we'll know soon enough.
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 8b86e53..f82ff32 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -38,6 +38,7 @@
         ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
         ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
         ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
+        ".avi",
     };
     static const size_t kNumValidExtensions =
         sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index e9e5ef9..76f47f7 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -425,6 +425,11 @@
         return false;
     }
 
+    sp<MediaExtractor> extractor = new WAVExtractor(source);
+    if (extractor->countTracks() == 0) {
+        return false;
+    }
+
     *mimeType = MEDIA_MIMETYPE_CONTAINER_WAV;
     *confidence = 0.3f;
 
diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
index 949a5e4..1096717 100644
--- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
+++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
@@ -79,6 +79,7 @@
     }
 
     mURI = uri;
+    mContentType = String8("application/octet-stream");
 
     if (headers != NULL) {
         mHeaders = *headers;
@@ -99,10 +100,12 @@
     return mState == CONNECTED ? OK : mIOResult;
 }
 
-void ChromiumHTTPDataSource::onConnectionEstablished(int64_t contentSize) {
+void ChromiumHTTPDataSource::onConnectionEstablished(
+        int64_t contentSize, const char *contentType) {
     Mutex::Autolock autoLock(mLock);
     mState = CONNECTED;
     mContentSize = (contentSize < 0) ? -1 : contentSize + mCurrentOffset;
+    mContentType = String8(contentType);
     mCondition.broadcast();
 }
 
@@ -314,6 +317,12 @@
     return String8(mURI.c_str());
 }
 
+String8 ChromiumHTTPDataSource::getMIMEType() const {
+    Mutex::Autolock autoLock(mLock);
+
+    return mContentType;
+}
+
 void ChromiumHTTPDataSource::clearDRMState_l() {
     if (mDecryptHandle != NULL) {
         // To release mDecryptHandle
diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp
index 7ac56e8..af2f6ac 100644
--- a/media/libstagefright/chromium_http/support.cpp
+++ b/media/libstagefright/chromium_http/support.cpp
@@ -253,7 +253,11 @@
 
     MY_LOGV(StringPrintf("response headers: %s", headers.c_str()).c_str());
 
-    mOwner->onConnectionEstablished(request->GetExpectedContentSize());
+    std::string contentType;
+    request->GetResponseHeaderByName("Content-Type", &contentType);
+
+    mOwner->onConnectionEstablished(
+            request->GetExpectedContentSize(), contentType.c_str());
 }
 
 void SfDelegate::OnReadCompleted(URLRequest *request, int bytes_read) {
diff --git a/media/libstagefright/include/AVIExtractor.h b/media/libstagefright/include/AVIExtractor.h
new file mode 100644
index 0000000..375a94d
--- /dev/null
+++ b/media/libstagefright/include/AVIExtractor.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AVI_EXTRACTOR_H_
+
+#define AVI_EXTRACTOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct AVIExtractor : public MediaExtractor {
+    AVIExtractor(const sp<DataSource> &dataSource);
+
+    virtual size_t countTracks();
+
+    virtual sp<MediaSource> getTrack(size_t index);
+
+    virtual sp<MetaData> getTrackMetaData(
+            size_t index, uint32_t flags);
+
+    virtual sp<MetaData> getMetaData();
+
+protected:
+    virtual ~AVIExtractor();
+
+private:
+    struct AVISource;
+
+    struct SampleInfo {
+        uint32_t mOffset;
+        bool mIsKey;
+    };
+
+    struct Track {
+        sp<MetaData> mMeta;
+        Vector<SampleInfo> mSamples;
+        uint32_t mRate;
+        uint32_t mScale;
+
+        enum Kind {
+            AUDIO,
+            VIDEO,
+            OTHER
+
+        } mKind;
+
+        size_t mNumSyncSamples;
+        size_t mThumbnailSampleSize;
+        ssize_t mThumbnailSampleIndex;
+        size_t mMaxSampleSize;
+    };
+
+    sp<DataSource> mDataSource;
+    status_t mInitCheck;
+    Vector<Track> mTracks;
+
+    off64_t mMovieOffset;
+    bool mFoundIndex;
+    bool mOffsetsAreAbsolute;
+
+    ssize_t parseChunk(off64_t offset, off64_t size, int depth = 0);
+    status_t parseStreamHeader(off64_t offset, size_t size);
+    status_t parseStreamFormat(off64_t offset, size_t size);
+    status_t parseIndex(off64_t offset, size_t size);
+
+    status_t parseHeaders();
+
+    status_t getSampleInfo(
+            size_t trackIndex, size_t sampleIndex,
+            off64_t *offset, size_t *size, bool *isKey);
+
+    status_t getSampleIndexAtTime(
+            size_t trackIndex,
+            int64_t timeUs, MediaSource::ReadOptions::SeekMode mode,
+            size_t *sampleIndex) const;
+
+    status_t addMPEG4CodecSpecificData(size_t trackIndex);
+
+    static bool IsCorrectChunkType(
+        ssize_t trackIndex, Track::Kind kind, uint32_t chunkType);
+
+    DISALLOW_EVIL_CONSTRUCTORS(AVIExtractor);
+};
+
+class String8;
+struct AMessage;
+
+bool SniffAVI(
+        const sp<DataSource> &source, String8 *mimeType, float *confidence,
+        sp<AMessage> *);
+
+}  // namespace android
+
+#endif  // AVI_EXTRACTOR_H_
diff --git a/media/libstagefright/include/ChromiumHTTPDataSource.h b/media/libstagefright/include/ChromiumHTTPDataSource.h
index af49059..0e2927d 100644
--- a/media/libstagefright/include/ChromiumHTTPDataSource.h
+++ b/media/libstagefright/include/ChromiumHTTPDataSource.h
@@ -51,6 +51,8 @@
 
     virtual String8 getUri();
 
+    virtual String8 getMIMEType() const;
+
 protected:
     virtual ~ChromiumHTTPDataSource();
 
@@ -90,6 +92,8 @@
 
     int64_t mContentSize;
 
+    String8 mContentType;
+
     List<BandwidthEntry> mBandwidthHistory;
     size_t mNumBandwidthHistoryItems;
     int64_t mTotalTransferTimeUs;
@@ -110,7 +114,9 @@
 
     void initiateRead(void *data, size_t size);
 
-    void onConnectionEstablished(int64_t contentSize);
+    void onConnectionEstablished(
+            int64_t contentSize, const char *contentType);
+
     void onConnectionFailed(status_t err);
     void onReadCompleted(ssize_t size);
     void onDisconnectComplete();
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 02d5817..2128682 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -40,6 +40,9 @@
     virtual sp<DecryptHandle> DrmInitialization();
     virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
     virtual String8 getUri();
+
+    virtual String8 getMIMEType() const;
+
     ////////////////////////////////////////////////////////////////////////////
 
     size_t cachedSize();
diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h
index 7dd5d59..2ab1f19 100644
--- a/media/libstagefright/include/NuHTTPDataSource.h
+++ b/media/libstagefright/include/NuHTTPDataSource.h
@@ -51,6 +51,8 @@
     virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
     virtual String8 getUri();
 
+    virtual String8 getMIMEType() const;
+
 protected:
     virtual ~NuHTTPDataSource();
 
@@ -85,6 +87,8 @@
     bool mContentLengthValid;
     bool mHasChunkedTransferEncoding;
 
+    String8 mContentType;
+
     // The number of data bytes in the current chunk before any subsequent
     // chunk header (or -1 if no more chunks).
     ssize_t mChunkDataBytesLeft;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 4fa5bcb..44f55b3 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -626,7 +626,7 @@
         }
 
         if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_DIALOG) {
-            showRecentAppsDialog();
+            showRecentAppsDialog(0);
         } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_ACTIVITY) {
             try {
                 Intent intent = new Intent();
@@ -645,12 +645,12 @@
     /**
      * Create (if necessary) and launch the recent apps dialog
      */
-    void showRecentAppsDialog() {
+    void showRecentAppsDialog(final int initialModifiers) {
         mHandler.post(new Runnable() {
             @Override
             public void run() {
                 if (mRecentAppsDialog == null) {
-                    mRecentAppsDialog = new RecentApplicationsDialog(mContext);
+                    mRecentAppsDialog = new RecentApplicationsDialog(mContext, initialModifiers);
                 }
                 mRecentAppsDialog.show();
             }
@@ -1392,7 +1392,7 @@
             return false;
         } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
             if (down && repeatCount == 0) {
-                showRecentAppsDialog();
+                showRecentAppsDialog(event.getMetaState() & KeyEvent.getModifierMetaStateMask());
             }
             return true;
         }
diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
index db66346..c4b7822 100644
--- a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
+++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java
@@ -17,16 +17,13 @@
 package com.android.internal.policy.impl;
 
 import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
 import android.app.Dialog;
-import android.app.IActivityManager;
 import android.app.StatusBarManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -34,6 +31,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
+import android.view.KeyEvent;
+import android.view.SoundEffectConstants;
 import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
@@ -72,13 +71,12 @@
         }
     };
 
-    private int mIconSize;
+    private int mInitialModifiers;
 
-    public RecentApplicationsDialog(Context context) {
+    public RecentApplicationsDialog(Context context, int initialModifiers) {
         super(context, com.android.internal.R.style.Theme_Dialog_RecentApplications);
 
-        final Resources resources = context.getResources();
-        mIconSize = (int) resources.getDimension(android.R.dimen.app_icon_size);
+        mInitialModifiers = initialModifiers;
     }
 
     /**
@@ -127,34 +125,102 @@
         }
     }
 
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_APP_SWITCH || keyCode == KeyEvent.KEYCODE_TAB) {
+            // Ignore all meta keys other than SHIFT.  The app switch key could be a
+            // fallback action chorded with ALT, META or even CTRL depending on the key map.
+            // DPad navigation is handled by the ViewRoot elsewhere.
+            final boolean backward = event.isShiftPressed();
+            final int numIcons = mIcons.length;
+            int numButtons = 0;
+            while (numButtons < numIcons && mIcons[numButtons].getVisibility() == View.VISIBLE) {
+                numButtons += 1;
+            }
+            if (numButtons != 0) {
+                int nextFocus = backward ? numButtons - 1 : 0;
+                for (int i = 0; i < numButtons; i++) {
+                    if (mIcons[i].hasFocus()) {
+                        if (backward) {
+                            nextFocus = (i + numButtons - 1) % numButtons;
+                        } else {
+                            nextFocus = (i + 1) % numButtons;
+                        }
+                        break;
+                    }
+                }
+                final int direction = backward ? View.FOCUS_BACKWARD : View.FOCUS_FORWARD;
+                if (mIcons[nextFocus].requestFocus(direction)) {
+                    mIcons[nextFocus].playSoundEffect(
+                            SoundEffectConstants.getContantForFocusDirection(direction));
+                }
+            }
+
+            // The dialog always handles the key to prevent the ViewRoot from
+            // performing the default navigation itself.
+            return true;
+        }
+
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (mInitialModifiers != 0 && event.hasNoModifiers()) {
+            final int numIcons = mIcons.length;
+            RecentTag tag = null;
+            for (int i = 0; i < numIcons; i++) {
+                if (mIcons[i].getVisibility() != View.VISIBLE) {
+                    break;
+                }
+                if (i == 0 || mIcons[i].hasFocus()) {
+                    tag = (RecentTag) mIcons[i].getTag();
+                    if (mIcons[i].hasFocus()) {
+                        break;
+                    }
+                }
+            }
+            if (tag != null) {
+                switchTo(tag);
+            }
+            dismiss();
+            return true;
+        }
+
+        return super.onKeyUp(keyCode, event);
+    }
+
     /**
      * Handler for user clicks.  If a button was clicked, launch the corresponding activity.
      */
     public void onClick(View v) {
-
         for (TextView b: mIcons) {
             if (b == v) {
                 RecentTag tag = (RecentTag)b.getTag();
-                if (tag.info.id >= 0) {
-                    // This is an active task; it should just go to the foreground.
-                    final ActivityManager am = (ActivityManager)
-                            getContext().getSystemService(Context.ACTIVITY_SERVICE);
-                    am.moveTaskToFront(tag.info.id, ActivityManager.MOVE_TASK_WITH_HOME);
-                } else if (tag.intent != null) {
-                    tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
-                            | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
-                    try {
-                        getContext().startActivity(tag.intent);
-                    } catch (ActivityNotFoundException e) {
-                        Log.w("Recent", "Unable to launch recent task", e);
-                    }
-                }
+                switchTo(tag);
                 break;
             }
         }
         dismiss();
     }
 
+    private void switchTo(RecentTag tag) {
+        if (tag.info.id >= 0) {
+            // This is an active task; it should just go to the foreground.
+            final ActivityManager am = (ActivityManager)
+                    getContext().getSystemService(Context.ACTIVITY_SERVICE);
+            am.moveTaskToFront(tag.info.id, ActivityManager.MOVE_TASK_WITH_HOME);
+        } else if (tag.intent != null) {
+            tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+                    | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+            try {
+                getContext().startActivity(tag.intent);
+            } catch (ActivityNotFoundException e) {
+                Log.w("Recent", "Unable to launch recent task", e);
+            }
+        }
+    }
+
     /**
      * Set up and show the recent activities dialog.
      */
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 6ea068a..8363e8b 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -186,7 +186,7 @@
     mPolicy(policy),
     mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
     mNextUnblockedEvent(NULL),
-    mDispatchEnabled(true), mDispatchFrozen(false),
+    mDispatchEnabled(true), mDispatchFrozen(false), mInputFilterEnabled(false),
     mFocusedWindow(NULL),
     mFocusedApplication(NULL),
     mCurrentInputTargetsValid(false),
@@ -725,7 +725,7 @@
         if (entry->repeatCount == 0
                 && entry->action == AKEY_EVENT_ACTION_DOWN
                 && (entry->policyFlags & POLICY_FLAG_TRUSTED)
-                && !entry->isInjected()) {
+                && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
             if (mKeyRepeatState.lastKeyEntry
                     && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
                 // We have seen two identical key downs in a row which indicates that the device
@@ -2402,7 +2402,18 @@
 
     bool needWake;
     { // acquire lock
-        AutoMutex _l(mLock);
+        mLock.lock();
+
+        if (mInputFilterEnabled) {
+            mLock.unlock();
+
+            policyFlags |= POLICY_FLAG_FILTERED;
+            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
+                return; // event was consumed by the filter
+            }
+
+            mLock.lock();
+        }
 
         int32_t repeatCount = 0;
         KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
@@ -2410,6 +2421,7 @@
                 metaState, repeatCount, downTime);
 
         needWake = enqueueInboundEventLocked(newEntry);
+        mLock.unlock();
     } // release lock
 
     if (needWake) {
@@ -2452,7 +2464,23 @@
 
     bool needWake;
     { // acquire lock
-        AutoMutex _l(mLock);
+        mLock.lock();
+
+        if (mInputFilterEnabled) {
+            mLock.unlock();
+
+            MotionEvent event;
+            event.initialize(deviceId, source, action, flags, edgeFlags, metaState, 0, 0,
+                    xPrecision, yPrecision, downTime, eventTime,
+                    pointerCount, pointerIds, pointerCoords);
+
+            policyFlags |= POLICY_FLAG_FILTERED;
+            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
+                return; // event was consumed by the filter
+            }
+
+            mLock.lock();
+        }
 
         // Attempt batching and streaming of move events.
         if (action == AMOTION_EVENT_ACTION_MOVE
@@ -2491,6 +2519,7 @@
                 LOGD("Appended motion sample onto batch for most recent "
                         "motion event for this device in the inbound queue.");
 #endif
+                mLock.unlock();
                 return; // done!
             }
 
@@ -2579,6 +2608,7 @@
                             true /*resumeWithAppendedMotionSample*/);
 
                     runCommandsLockedInterruptible();
+                    mLock.unlock();
                     return; // done!
                 }
             }
@@ -2593,6 +2623,7 @@
                 pointerCount, pointerIds, pointerCoords);
 
         needWake = enqueueInboundEventLocked(newEntry);
+        mLock.unlock();
     } // release lock
 
     if (needWake) {
@@ -2612,16 +2643,17 @@
 }
 
 int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
-        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+        int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+        uint32_t policyFlags) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
-            "syncMode=%d, timeoutMillis=%d",
-            event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis);
+            "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
+            event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
 #endif
 
     nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
 
-    uint32_t policyFlags = POLICY_FLAG_INJECTED;
+    policyFlags |= POLICY_FLAG_INJECTED;
     if (hasInjectionPermission(injectorPid, injectorUid)) {
         policyFlags |= POLICY_FLAG_TRUSTED;
     }
@@ -2640,7 +2672,9 @@
             policyFlags |= POLICY_FLAG_VIRTUAL;
         }
 
-        mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
+        if (!(policyFlags & POLICY_FLAG_FILTERED)) {
+            mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);
+        }
 
         if (policyFlags & POLICY_FLAG_WOKE_HERE) {
             flags |= AKEY_EVENT_FLAG_WOKE_HERE;
@@ -2664,8 +2698,10 @@
             return INPUT_EVENT_INJECTION_FAILED;
         }
 
-        nsecs_t eventTime = motionEvent->getEventTime();
-        mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
+        if (!(policyFlags & POLICY_FLAG_FILTERED)) {
+            nsecs_t eventTime = motionEvent->getEventTime();
+            mPolicy->interceptMotionBeforeQueueing(eventTime, /*byref*/ policyFlags);
+        }
 
         mLock.lock();
         const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
@@ -2780,7 +2816,8 @@
                  injectionResult, injectionState->injectorPid, injectionState->injectorUid);
 #endif
 
-        if (injectionState->injectionIsAsync) {
+        if (injectionState->injectionIsAsync
+                && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
             // Log the outcome since the injector did not wait for the injection result.
             switch (injectionResult) {
             case INPUT_EVENT_INJECTION_SUCCEEDED:
@@ -2982,6 +3019,26 @@
     }
 }
 
+void InputDispatcher::setInputFilterEnabled(bool enabled) {
+#if DEBUG_FOCUS
+    LOGD("setInputFilterEnabled: enabled=%d", enabled);
+#endif
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        if (mInputFilterEnabled == enabled) {
+            return;
+        }
+
+        mInputFilterEnabled = enabled;
+        resetAndDropEverythingLocked("input filter is being enabled or disabled");
+    } // release lock
+
+    // Wake up poll loop since there might be work to do to drop everything.
+    mLooper->wake();
+}
+
 bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
         const sp<InputChannel>& toChannel) {
 #if DEBUG_FOCUS
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 48e4d43..162e606 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -176,6 +176,13 @@
      */
     virtual int32_t getMaxEventsPerSecond() = 0;
 
+    /* Filters an input event.
+     * Return true to dispatch the event unmodified, false to consume the event.
+     * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
+     * to injectInputEvent.
+     */
+    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0;
+
     /* Intercepts a key event immediately before queueing it.
      * The policy can use this method as an opportunity to perform power management functions
      * and early event preprocessing such as updating policy flags.
@@ -266,7 +273,8 @@
      * This method may be called on any thread (usually by the input manager).
      */
     virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0;
+            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+            uint32_t policyFlags) = 0;
 
     /* Sets the list of input windows.
      *
@@ -286,6 +294,14 @@
      */
     virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
 
+    /* Sets whether input event filtering is enabled.
+     * When enabled, incoming input events are sent to the policy's filterInputEvent
+     * method instead of being dispatched.  The filter is expected to use
+     * injectInputEvent to inject the events it would like to have dispatched.
+     * It should include POLICY_FLAG_FILTERED in the policy flags during injection.
+     */
+    virtual void setInputFilterEnabled(bool enabled) = 0;
+
     /* Transfers touch focus from the window associated with one channel to the
      * window associated with the other channel.
      *
@@ -345,11 +361,13 @@
             int32_t switchCode, int32_t switchValue, uint32_t policyFlags) ;
 
     virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis);
+            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+            uint32_t policyFlags);
 
     virtual void setInputWindows(const Vector<InputWindow>& inputWindows);
     virtual void setFocusedApplication(const InputApplication* inputApplication);
     virtual void setInputDispatchMode(bool enabled, bool frozen);
+    virtual void setInputFilterEnabled(bool enabled);
 
     virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
             const sp<InputChannel>& toChannel);
@@ -863,6 +881,7 @@
     // Dispatch state.
     bool mDispatchEnabled;
     bool mDispatchFrozen;
+    bool mInputFilterEnabled;
 
     Vector<InputWindow> mWindows;
 
diff --git a/services/input/tests/InputDispatcher_test.cpp b/services/input/tests/InputDispatcher_test.cpp
index 2f846c4..3650da0 100644
--- a/services/input/tests/InputDispatcher_test.cpp
+++ b/services/input/tests/InputDispatcher_test.cpp
@@ -67,6 +67,10 @@
         return 60;
     }
 
+    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+        return true;
+    }
+
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) {
     }
 
@@ -124,7 +128,7 @@
             /*action*/ -1, 0,
             AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject key events with undefined action.";
 
     // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
@@ -132,7 +136,7 @@
             AKEY_EVENT_ACTION_MULTIPLE, 0,
             AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject key events with ACTION_MULTIPLE.";
 }
 
@@ -150,7 +154,7 @@
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 1, pointerIds, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with undefined action.";
 
     // Rejects pointer down with invalid index.
@@ -160,7 +164,7 @@
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 1, pointerIds, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer down index too large.";
 
     event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
@@ -169,7 +173,7 @@
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 1, pointerIds, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer down index too small.";
 
     // Rejects pointer up with invalid index.
@@ -179,7 +183,7 @@
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 1, pointerIds, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer up index too large.";
 
     event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
@@ -188,7 +192,7 @@
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 1, pointerIds, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer up index too small.";
 
     // Rejects motion events with invalid number of pointers.
@@ -197,7 +201,7 @@
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 0, pointerIds, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with 0 pointers.";
 
     event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
@@ -205,7 +209,7 @@
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ MAX_POINTERS + 1, pointerIds, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with more than MAX_POINTERS pointers.";
 
     // Rejects motion events with invalid pointer ids.
@@ -215,7 +219,7 @@
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 1, pointerIds, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer ids less than 0.";
 
     pointerIds[0] = MAX_POINTER_ID + 1;
@@ -224,7 +228,7 @@
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 1, pointerIds, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
 
     // Rejects motion events with duplicate pointer ids.
@@ -235,7 +239,7 @@
             ARBITRARY_TIME, ARBITRARY_TIME,
             /*pointerCount*/ 2, pointerIds, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with duplicate pointer ids.";
 }
 
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 60549c6..4c5f239 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -369,7 +369,8 @@
     }
 
     virtual int32_t injectInputEvent(const InputEvent* event,
-            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+            int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+            uint32_t policyFlags) {
         ADD_FAILURE() << "Should never be called by input reader.";
         return INPUT_EVENT_INJECTION_FAILED;
     }
@@ -386,6 +387,10 @@
         ADD_FAILURE() << "Should never be called by input reader.";
     }
 
+    virtual void setInputFilterEnabled(bool enabled) {
+        ADD_FAILURE() << "Should never be called by input reader.";
+    }
+
     virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
             const sp<InputChannel>& toChannel) {
         ADD_FAILURE() << "Should never be called by input reader.";
diff --git a/services/java/com/android/server/LightsService.java b/services/java/com/android/server/LightsService.java
index 21f2bcf..1e95f3e 100644
--- a/services/java/com/android/server/LightsService.java
+++ b/services/java/com/android/server/LightsService.java
@@ -148,7 +148,6 @@
                 fis.close();
                 return (result != '0');
             } catch (Exception e) {
-                Slog.e(TAG, "getFlashlightEnabled failed", e);
                 return false;
             }
         }
@@ -168,7 +167,7 @@
                 fos.write(bytes);
                 fos.close();
             } catch (Exception e) {
-                Slog.e(TAG, "setFlashlightEnabled failed", e);
+                // fail silently
             }
         }
     };
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4e1bbac..cd8915d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.usb.UsbService;
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index 2fcdb5d..c39dc80 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -247,6 +247,7 @@
     // Lock held on mVibrations
     private void startNextVibrationLocked() {
         if (mVibrations.size() <= 0) {
+            mCurrentVibration = null;
             return;
         }
         mCurrentVibration = mVibrations.getFirst();
@@ -273,17 +274,27 @@
             Vibration vib = iter.next();
             if (vib.mToken == token) {
                 iter.remove();
+                unlinkVibration(vib);
                 return vib;
             }
         }
         // We might be looking for a simple vibration which is only stored in
         // mCurrentVibration.
         if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
+            unlinkVibration(mCurrentVibration);
             return mCurrentVibration;
         }
         return null;
     }
 
+    private void unlinkVibration(Vibration vib) {
+        if (vib.mPattern != null) {
+            // If Vibration object has a pattern,
+            // the Vibration object has also been linkedToDeath.
+            vib.mToken.unlinkToDeath(vib, 0);
+        }
+    }
+
     private class VibrateThread extends Thread {
         final Vibration mVibration;
         boolean mDone;
@@ -360,6 +371,7 @@
                     // If this vibration finished naturally, start the next
                     // vibration.
                     mVibrations.remove(mVibration);
+                    unlinkVibration(mVibration);
                     startNextVibrationLocked();
                 }
             }
diff --git a/services/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
new file mode 100644
index 0000000..ced8feb
--- /dev/null
+++ b/services/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import com.android.server.wm.InputFilter;
+
+import android.content.Context;
+import android.util.Slog;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy;
+
+/**
+ * Input filter for accessibility.
+ *
+ * Currently just a stub but will eventually implement touch exploration, etc.
+ */
+public class AccessibilityInputFilter extends InputFilter {
+    private static final String TAG = "AccessibilityInputFilter";
+    private static final boolean DEBUG = true;
+
+    private final Context mContext;
+
+    public AccessibilityInputFilter(Context context) {
+        super(context.getMainLooper());
+        mContext = context;
+    }
+
+    @Override
+    public void onInstalled() {
+        if (DEBUG) {
+            Slog.d(TAG, "Accessibility input filter installed.");
+        }
+        super.onInstalled();
+    }
+
+    @Override
+    public void onUninstalled() {
+        if (DEBUG) {
+            Slog.d(TAG, "Accessibility input filter uninstalled.");
+        }
+        super.onUninstalled();
+    }
+
+    @Override
+    public void onInputEvent(InputEvent event, int policyFlags) {
+        if (DEBUG) {
+            Slog.d(TAG, "Accessibility input filter received input event: "
+                    + event + ", policyFlags=0x" + Integer.toHexString(policyFlags));
+        }
+
+        // To prove that this is working as intended, we will silently transform
+        // Q key presses into non-repeating Z's as part of this stub implementation.
+        // TODO: Replace with the real thing.
+        if (event instanceof KeyEvent) {
+            final KeyEvent keyEvent = (KeyEvent)event;
+            if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_Q) {
+                if (keyEvent.getRepeatCount() == 0) {
+                    sendInputEvent(new KeyEvent(keyEvent.getDownTime(), keyEvent.getEventTime(),
+                            keyEvent.getAction(), KeyEvent.KEYCODE_Z, keyEvent.getRepeatCount(),
+                            keyEvent.getMetaState(), keyEvent.getDeviceId(), keyEvent.getScanCode(),
+                            keyEvent.getFlags(), keyEvent.getSource()),
+                            policyFlags | WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
+                }
+                return;
+            }
+        }
+
+        super.onInputEvent(event, policyFlags);
+    }
+}
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
similarity index 96%
rename from services/java/com/android/server/AccessibilityManagerService.java
rename to services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 04ae490..5257fb0 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -14,11 +14,12 @@
  ** limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.accessibility;
 
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.HandlerCaller.SomeArgs;
+import com.android.server.wm.WindowManagerService;
 
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -43,6 +44,7 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.TextUtils.SimpleStringSplitter;
@@ -106,6 +108,7 @@
     private int mHandledFeedbackTypes = 0;
 
     private boolean mIsEnabled;
+    private AccessibilityInputFilter mInputFilter;
 
     /**
      * Handler for delayed event dispatch.
@@ -131,7 +134,7 @@
      *
      * @param context A {@link Context} instance.
      */
-    AccessibilityManagerService(Context context) {
+    public AccessibilityManagerService(Context context) {
         mContext = context;
         mPackageManager = mContext.getPackageManager();
         mCaller = new HandlerCaller(context, this);
@@ -209,6 +212,7 @@
                         }
 
                         manageServicesLocked();
+                        updateInputFilterLocked();
                     }
                     
                     return;
@@ -249,6 +253,7 @@
                             unbindAllServicesLocked();
                         }
                         updateClientsLocked();
+                        updateInputFilterLocked();
                     }
                 }
             });
@@ -621,6 +626,25 @@
     }
 
     /**
+     * Installs or removes the accessibility input filter when accessibility is enabled
+     * or disabled.
+     */
+    private void updateInputFilterLocked() {
+        WindowManagerService wm = (WindowManagerService)ServiceManager.getService(
+                Context.WINDOW_SERVICE);
+        if (wm != null) {
+            if (mIsEnabled) {
+                if (mInputFilter == null) {
+                    mInputFilter = new AccessibilityInputFilter(mContext);
+                }
+                wm.setInputFilter(mInputFilter);
+            } else {
+                wm.setInputFilter(null);
+            }
+        }
+    }
+
+    /**
      * This class represents an accessibility service. It stores all per service
      * data required for the service management, provides API for starting/stopping the
      * service and is responsible for adding/removing the service in the data structures
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index b7f6346..a151af0 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -561,11 +561,14 @@
                     case MSG_UPDATE_STATE:
                         if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
                             if (mConnected == 0) {
-                                // make sure accessory mode is off, and restore default functions
-                                if (mCurrentAccessory != null && UsbManager.setFunctionEnabled(
-                                        UsbManager.USB_FUNCTION_ACCESSORY, false)) {
+                                if (UsbManager.isFunctionEnabled(
+                                            UsbManager.USB_FUNCTION_ACCESSORY)) {
+                                    // make sure accessory mode is off, and restore default functions
                                     Log.d(TAG, "exited USB accessory mode");
-
+                                    if (!UsbManager.setFunctionEnabled
+                                            (UsbManager.USB_FUNCTION_ACCESSORY, false)) {
+                                        Log.e(TAG, "could not disable accessory function");
+                                    }
                                     int count = mDefaultFunctions.size();
                                     for (int i = 0; i < count; i++) {
                                         String function = mDefaultFunctions.get(i);
@@ -574,8 +577,10 @@
                                         }
                                     }
 
-                                    mDeviceManager.accessoryDetached(mCurrentAccessory);
-                                    mCurrentAccessory = null;
+                                    if (mCurrentAccessory != null) {
+                                        mDeviceManager.accessoryDetached(mCurrentAccessory);
+                                        mCurrentAccessory = null;
+                                    }
                                 }
                             }
 
diff --git a/services/java/com/android/server/wm/InputFilter.java b/services/java/com/android/server/wm/InputFilter.java
new file mode 100644
index 0000000..78b87fe
--- /dev/null
+++ b/services/java/com/android/server/wm/InputFilter.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManagerPolicy;
+
+/**
+ * Filters input events before they are dispatched to the system.
+ * <p>
+ * At most one input filter can be installed by calling
+ * {@link WindowManagerService#setInputFilter}.  When an input filter is installed, the
+ * system's behavior changes as follows:
+ * <ul>
+ * <li>Input events are first delivered to the {@link WindowManagerPolicy}
+ * interception methods before queueing as usual.  This critical step takes care of managing
+ * the power state of the device and handling wake keys.</li>
+ * <li>Input events are then asynchronously delivered to the input filter's
+ * {@link #onInputEvent(InputEvent)} method instead of being enqueued for dispatch to
+ * applications as usual.  The input filter only receives input events that were
+ * generated by input device; the input filter will not receive input events that were
+ * injected into the system by other means, such as by instrumentation.</li>
+ * <li>The input filter processes and optionally transforms the stream of events.  For example,
+ * it may transform a sequence of motion events representing an accessibility gesture into
+ * a different sequence of motion events, key presses or other system-level interactions.
+ * The input filter can send events to be dispatched by calling
+ * {@link #sendInputEvent(InputEvent)} and passing appropriate policy flags for the
+ * input event.</li>
+ * </ul>
+ * </p>
+ * <h3>The importance of input event consistency</h3>
+ * <p>
+ * The input filter mechanism is very low-level.  At a minimum, it needs to ensure that it
+ * sends an internally consistent stream of input events to the dispatcher.  There are
+ * very important invariants to be maintained.
+ * </p><p>
+ * For example, if a key down is sent, a corresponding key up should also be sent eventually.
+ * Likewise, for touch events, each pointer must individually go down with
+ * {@link MotionEvent#ACTION_DOWN} or {@link MotionEvent#ACTION_POINTER_DOWN} and then
+ * individually go up with {@link MotionEvent#ACTION_POINTER_UP} or {@link MotionEvent#ACTION_UP}
+ * and the sequence of pointer ids used must be consistent throughout the gesture.
+ * </p><p>
+ * Sometimes a filter may wish to cancel a previously dispatched key or motion.  It should
+ * use {@link KeyEvent#FLAG_CANCELED} or {@link MotionEvent#ACTION_CANCEL} accordingly.
+ * </p><p>
+ * The input filter must take into account the fact that the input events coming from different
+ * devices or even different sources all consist of distinct streams of input.
+ * Use {@link InputEvent#getDeviceId()} and {@link InputEvent#getSource()} to identify
+ * the source of the event and its semantics.  There are be multiple sources of keys,
+ * touches and other input: they must be kept separate.
+ * </p>
+ * <h3>Policy flags</h3>
+ * <p>
+ * Input events received from the dispatcher and sent to the dispatcher have policy flags
+ * associated with them.  Policy flags control some functions of the dispatcher.
+ * </p><p>
+ * The early policy interception decides whether an input event should be delivered
+ * to applications or dropped.  The policy indicates its decision by setting the
+ * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} policy flag.  The input filter may
+ * sometimes receive events that do not have this flag set.  It should take note of
+ * the fact that the policy intends to drop the event, clean up its state, and
+ * then send appropriate cancelation events to the dispatcher if needed.
+ * </p><p>
+ * For example, suppose the input filter is processing a gesture and one of the touch events
+ * it receives does not have the {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag set.
+ * The input filter should clear its internal state about the gesture and then send key or
+ * motion events to the dispatcher to cancel any keys or pointers that are down.
+ * </p><p>
+ * Corollary: Events that set sent to the dispatcher should usually include the
+ * {@link WindowManagerPolicy#FLAG_PASS_TO_USER} flag.  Otherwise, they will be dropped!
+ * </p><p>
+ * It may be prudent to disable automatic key repeating for synthetically generated
+ * keys by setting the {@link WindowManagerPolicy#FLAG_DISABLE_KEY_REPEAT} policy flag.
+ * </p>
+ */
+public abstract class InputFilter {
+    private static final int MSG_INSTALL = 1;
+    private static final int MSG_UNINSTALL = 2;
+    private static final int MSG_INPUT_EVENT = 3;
+
+    private final H mH;
+    private Host mHost;
+
+    /**
+     * Creates the input filter.
+     *
+     * @param looper The looper to run callbacks on.
+     */
+    public InputFilter(Looper looper) {
+        mH = new H(looper);
+    }
+
+    /**
+     * Called when the input filter is installed.
+     * This method is guaranteed to be non-reentrant.
+     *
+     * @param host The input filter host environment.
+     */
+    final void install(Host host) {
+        mH.obtainMessage(MSG_INSTALL, host).sendToTarget();
+    }
+
+    /**
+     * Called when the input filter is uninstalled.
+     * This method is guaranteed to be non-reentrant.
+     */
+    final void uninstall() {
+        mH.obtainMessage(MSG_UNINSTALL).sendToTarget();
+    }
+
+    /**
+     * Called to enqueue the input event for filtering.
+     * The event will be recycled after the input filter processes it.
+     * This method is guaranteed to be non-reentrant.
+     *
+     * @param event The input event to enqueue.
+     */
+    final void filterInputEvent(InputEvent event, int policyFlags) {
+        mH.obtainMessage(MSG_INPUT_EVENT, policyFlags, 0, event).sendToTarget();
+    }
+
+    /**
+     * Sends an input event to the dispatcher.
+     *
+     * @param event The input event to publish.
+     * @param policyFlags The input event policy flags.
+     */
+    public void sendInputEvent(InputEvent event, int policyFlags) {
+        if (event == null) {
+            throw new IllegalArgumentException("event must not be null");
+        }
+        if (mHost == null) {
+            throw new IllegalStateException("Cannot send input event because the input filter " +
+                    "is not installed.");
+        }
+        mHost.sendInputEvent(event, policyFlags);
+    }
+
+    /**
+     * Called when an input event has been received from the dispatcher.
+     * <p>
+     * The default implementation sends the input event back to the dispatcher, unchanged.
+     * </p><p>
+     * The event will be recycled when this method returns.  If you want to keep it around,
+     * make a copy!
+     * </p>
+     *
+     * @param event The input event that was received.
+     * @param policyFlags The input event policy flags.
+     */
+    public void onInputEvent(InputEvent event, int policyFlags) {
+        sendInputEvent(event, policyFlags);
+    }
+
+    /**
+     * Called when the filter is installed into the dispatch pipeline.
+     * <p>
+     * This method is called before the input filter receives any input events.
+     * The input filter should take this opportunity to prepare itself.
+     * </p>
+     */
+    public void onInstalled() {
+    }
+
+    /**
+     * Called when the filter is uninstalled from the dispatch pipeline.
+     * <p>
+     * This method is called after the input filter receives its last input event.
+     * The input filter should take this opportunity to clean up.
+     * </p>
+     */
+    public void onUninstalled() {
+    }
+
+    private final class H extends Handler {
+        public H(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_INSTALL:
+                    mHost = (Host)msg.obj;
+                    onInstalled();
+                    break;
+
+                case MSG_UNINSTALL:
+                    try {
+                        onUninstalled();
+                    } finally {
+                        mHost = null;
+                    }
+                    break;
+
+                case MSG_INPUT_EVENT: {
+                    final InputEvent event = (InputEvent)msg.obj;
+                    try {
+                        onInputEvent(event, msg.arg1);
+                    } finally {
+                        event.recycle();
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+    interface Host {
+        public void sendInputEvent(InputEvent event, int policyFlags);
+    }
+}
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index ca1da95..b0978a3 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -42,6 +42,7 @@
 import android.view.Surface;
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -78,8 +79,10 @@
     private static native void nativeRegisterInputChannel(InputChannel inputChannel,
             InputWindowHandle inputWindowHandle, boolean monitor);
     private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
+    private static native void nativeSetInputFilterEnabled(boolean enable);
     private static native int nativeInjectInputEvent(InputEvent event,
-            int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
+            int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
+            int policyFlags);
     private static native void nativeSetInputWindows(InputWindow[] windows);
     private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
     private static native void nativeSetSystemUiVisibility(int visibility);
@@ -117,6 +120,11 @@
     /** The key is down but is a virtual key press that is being emulated by the system. */
     public static final int KEY_STATE_VIRTUAL = 2;
 
+    // State for the currently installed input filter.
+    final Object mInputFilterLock = new Object();
+    InputFilter mInputFilter;
+    InputFilterHost mInputFilterHost;
+
     public InputManager(Context context, WindowManagerService windowManagerService) {
         this.mContext = context;
         this.mWindowManagerService = windowManagerService;
@@ -268,7 +276,42 @@
         
         nativeUnregisterInputChannel(inputChannel);
     }
-    
+
+    /**
+     * Sets an input filter that will receive all input events before they are dispatched.
+     * The input filter may then reinterpret input events or inject new ones.
+     *
+     * To ensure consistency, the input dispatcher automatically drops all events
+     * in progress whenever an input filter is installed or uninstalled.  After an input
+     * filter is uninstalled, it can no longer send input events unless it is reinstalled.
+     * Any events it attempts to send after it has been uninstalled will be dropped.
+     *
+     * @param filter The input filter, or null to remove the current filter.
+     */
+    public void setInputFilter(InputFilter filter) {
+        synchronized (mInputFilterLock) {
+            final InputFilter oldFilter = mInputFilter;
+            if (oldFilter == filter) {
+                return; // nothing to do
+            }
+
+            if (oldFilter != null) {
+                mInputFilter = null;
+                mInputFilterHost.disconnectLocked();
+                mInputFilterHost = null;
+                oldFilter.uninstall();
+            }
+
+            if (filter != null) {
+                mInputFilter = filter;
+                mInputFilterHost = new InputFilterHost();
+                filter.install(mInputFilterHost);
+            }
+
+            nativeSetInputFilterEnabled(filter != null);
+        }
+    }
+
     /**
      * Injects an input event into the event system on behalf of an application.
      * The synchronization mode determines whether the method blocks while waiting for
@@ -304,9 +347,10 @@
             throw new IllegalArgumentException("timeoutMillis must be positive");
         }
 
-        return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis);
+        return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis,
+                WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
     }
-    
+
     /**
      * Gets information about the input device with the specified id.
      * @param id The device id.
@@ -370,6 +414,27 @@
         }
     }
 
+    private final class InputFilterHost implements InputFilter.Host {
+        private boolean mDisconnected;
+
+        public void disconnectLocked() {
+            mDisconnected = true;
+        }
+
+        public void sendInputEvent(InputEvent event, int policyFlags) {
+            if (event == null) {
+                throw new IllegalArgumentException("event must not be null");
+            }
+
+            synchronized (mInputFilterLock) {
+                if (!mDisconnected) {
+                    nativeInjectInputEvent(event, 0, 0, INPUT_EVENT_INJECTION_SYNC_NONE, 0,
+                            policyFlags | WindowManagerPolicy.FLAG_FILTERED);
+                }
+            }
+        }
+    }
+
     private static final class PointerIcon {
         public Bitmap bitmap;
         public float hotSpotX;
@@ -415,7 +480,7 @@
     /*
      * Callbacks from native.
      */
-    private class Callbacks {
+    private final class Callbacks {
         static final String TAG = "InputManager-Callbacks";
         
         private static final boolean DEBUG_VIRTUAL_KEYS = false;
@@ -443,7 +508,19 @@
             return mWindowManagerService.mInputMonitor.notifyANR(
                     inputApplicationHandle, inputWindowHandle);
         }
-        
+
+        @SuppressWarnings("unused")
+        final boolean filterInputEvent(InputEvent event, int policyFlags) {
+            synchronized (mInputFilterLock) {
+                if (mInputFilter != null) {
+                    mInputFilter.filterInputEvent(event, policyFlags);
+                    return false;
+                }
+            }
+            event.recycle();
+            return true;
+        }
+
         @SuppressWarnings("unused")
         public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
             return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 33e6a36..79c4518 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4604,6 +4604,10 @@
         return mInputManager.monitorInput(inputChannelName);
     }
 
+    public void setInputFilter(InputFilter filter) {
+        mInputManager.setInputFilter(filter);
+    }
+
     public InputDevice getInputDevice(int deviceId) {
         return mInputManager.getInputDevice(deviceId);
     }
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 96cf4bd..ab2c125 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -56,6 +56,7 @@
     jmethodID notifyLidSwitchChanged;
     jmethodID notifyInputChannelBroken;
     jmethodID notifyANR;
+    jmethodID filterInputEvent;
     jmethodID interceptKeyBeforeQueueing;
     jmethodID interceptMotionBeforeQueueingWhenScreenOff;
     jmethodID interceptKeyBeforeDispatching;
@@ -174,6 +175,7 @@
     virtual nsecs_t getKeyRepeatTimeout();
     virtual nsecs_t getKeyRepeatDelay();
     virtual int32_t getMaxEventsPerSecond();
+    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
     virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags);
     virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
@@ -638,6 +640,38 @@
     return android_server_PowerManagerService_isScreenBright();
 }
 
+bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+    jobject inputEventObj;
+
+    JNIEnv* env = jniEnv();
+    switch (inputEvent->getType()) {
+    case AINPUT_EVENT_TYPE_KEY:
+        inputEventObj = android_view_KeyEvent_fromNative(env,
+                static_cast<const KeyEvent*>(inputEvent));
+        break;
+    case AINPUT_EVENT_TYPE_MOTION:
+        inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
+                static_cast<const MotionEvent*>(inputEvent));
+        break;
+    default:
+        return true; // dispatch the event normally
+    }
+
+    if (!inputEventObj) {
+        LOGE("Failed to obtain input event object for filterInputEvent.");
+        return true; // dispatch the event normally
+    }
+
+    // The callee is responsible for recycling the event.
+    jboolean pass = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.filterInputEvent,
+            inputEventObj, policyFlags);
+    if (checkAndClearExceptionFromCallback(env, "filterInputEvent")) {
+        pass = true;
+    }
+    env->DeleteLocalRef(inputEventObj);
+    return pass;
+}
+
 void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
         uint32_t& policyFlags) {
     // Policy:
@@ -1005,9 +1039,18 @@
     }
 }
 
+static void android_server_InputManager_nativeSetInputFilterEnabled(JNIEnv* env, jclass clazz,
+        jboolean enabled) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    gNativeInputManager->getInputManager()->getDispatcher()->setInputFilterEnabled(enabled);
+}
+
 static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jclass clazz,
         jobject inputEventObj, jint injectorPid, jint injectorUid,
-        jint syncMode, jint timeoutMillis) {
+        jint syncMode, jint timeoutMillis, jint policyFlags) {
     if (checkInputManagerUnitialized(env)) {
         return INPUT_EVENT_INJECTION_FAILED;
     }
@@ -1021,7 +1064,8 @@
         }
 
         return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
-                & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
+                & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis,
+                uint32_t(policyFlags));
     } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
         const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj);
         if (!motionEvent) {
@@ -1030,7 +1074,8 @@
         }
 
         return gNativeInputManager->getInputManager()->getDispatcher()->injectInputEvent(
-                motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis);
+                motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis,
+                uint32_t(policyFlags));
     } else {
         jniThrowRuntimeException(env, "Invalid input event type.");
         return INPUT_EVENT_INJECTION_FAILED;
@@ -1200,7 +1245,9 @@
             (void*) android_server_InputManager_nativeRegisterInputChannel },
     { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
             (void*) android_server_InputManager_nativeUnregisterInputChannel },
-    { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I",
+    { "nativeSetInputFilterEnabled", "(Z)V",
+            (void*) android_server_InputManager_nativeSetInputFilterEnabled },
+    { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIIII)I",
             (void*) android_server_InputManager_nativeInjectInputEvent },
     { "nativeSetInputWindows", "([Lcom/android/server/wm/InputWindow;)V",
             (void*) android_server_InputManager_nativeSetInputWindows },
@@ -1257,6 +1304,9 @@
             "notifyANR",
             "(Lcom/android/server/wm/InputApplicationHandle;Lcom/android/server/wm/InputWindowHandle;)J");
 
+    GET_METHOD_ID(gCallbacksClassInfo.filterInputEvent, clazz,
+            "filterInputEvent", "(Landroid/view/InputEvent;I)Z");
+
     GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, clazz,
             "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I");
 
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index b5578c3..e21e951 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -109,14 +109,17 @@
      * Used internally for saving disconnecting parameters.
      */
     protected static class DisconnectParams {
-        public DisconnectParams(Message onCompletedMsg) {
+        public DisconnectParams(String reason, Message onCompletedMsg) {
+            this.reason = reason;
             this.onCompletedMsg = onCompletedMsg;
         }
         public DisconnectParams(ResetSynchronouslyLock lockObj) {
+            this.reason = null;
             this.lockObj = lockObj;
         }
 
         public int tag;
+        public String reason;
         public Message onCompletedMsg;
         public ResetSynchronouslyLock lockObj;
     }
@@ -274,10 +277,8 @@
         if ((o != null) && (o instanceof DisconnectParams)) {
             DisconnectParams dp = (DisconnectParams)o;
             Message m = dp.onCompletedMsg;
-            if ((m != null) && (m.obj != null) && (m.obj instanceof String)) {
-                String reason = (String)m.obj;
-                if (TextUtils.equals(reason, Phone.REASON_RADIO_TURNED_OFF))
-                    discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
+            if (TextUtils.equals(dp.reason, Phone.REASON_RADIO_TURNED_OFF)) {
+                discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
             }
         }
         if (phone.mCM.getRadioState().isOn()) {
@@ -963,7 +964,7 @@
      *        With AsyncResult.userObj set to the original msg.obj.
      */
     public void reset(Message onCompletedMsg) {
-        sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(onCompletedMsg)));
+        sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(null, onCompletedMsg)));
     }
 
     /**
@@ -1014,8 +1015,8 @@
      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
      *        With AsyncResult.userObj set to the original msg.obj.
      */
-    public void disconnect(Message onCompletedMsg) {
-        sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(onCompletedMsg)));
+    public void disconnect(String reason, Message onCompletedMsg) {
+        sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(reason, onCompletedMsg)));
     }
 
     // ****** The following are used for debugging.
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index be97124..fba73fb5 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -42,6 +42,7 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -244,9 +245,26 @@
     protected HashMap<Integer, DataConnection> mDataConnections =
         new HashMap<Integer, DataConnection>();
 
+    /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
+    protected HashMap<String, Integer> mApnToDataConnectionId =
+                                    new HashMap<String, Integer>();
+
+    /** Phone.APN_TYPE_* ===> ApnContext */
+    protected ConcurrentHashMap<String, ApnContext> mApnContexts;
+
     /* Currently active APN */
     protected ApnSetting mActiveApn;
 
+    /** allApns holds all apns */
+    protected ArrayList<ApnSetting> mAllApns = null;
+
+    /** preferred apn */
+    protected ApnSetting mPreferredApn = null;
+
+    /** Is packet service restricted by network */
+    protected boolean mIsPsRestricted = false;
+
+
     /* Once disposed dont handle any messages */
     protected boolean mIsDisposed = false;
 
@@ -344,11 +362,6 @@
         return mActivity;
     }
 
-    public State getState() {
-        // TODO: reimplement to use apnType better yet REMOVE.
-        return mState;
-    }
-
     /**
      * @return the data connections
      */
@@ -390,16 +403,7 @@
         return result;
     }
 
-    private String getActiveApnType() {
-        String result;
-        if (mActiveApn != null) {
-            result = apnIdToType(mActiveApn.id);
-        } else {
-            result = null;
-        }
-        return result;
-    }
-
+    /** TODO: See if we can remove */
     public String getActiveApnString() {
         String result = null;
         if (mActiveApn != null) {
@@ -434,10 +438,19 @@
         }
     }
 
-
+    // abstract methods
     protected abstract String getActionIntentReconnectAlarm();
+    protected abstract void startNetStatPoll();
+    protected abstract void stopNetStatPoll();
+    protected abstract void restartRadio();
+    protected abstract void log(String s);
+    protected abstract void loge(String s);
+    protected abstract boolean isDataAllowed();
+    protected abstract boolean isApnTypeAvailable(String type);
+    public    abstract State getState(String apnType);
+    protected abstract void setState(State s);
+    protected abstract void gotoIdleAndNotifyDataConnection(String reason);
 
-    // abstract handler methods
     protected abstract boolean onTrySetupData(String reason);
     protected abstract void onRoamingOff();
     protected abstract void onRoamingOn();
@@ -448,7 +461,7 @@
     protected abstract void onVoiceCallStarted();
     protected abstract void onVoiceCallEnded();
     protected abstract void onCleanUpConnection(boolean tearDown, int apnId, String reason);
-    protected abstract void onCleanUpAllConnections();
+    protected abstract void onCleanUpAllConnections(String cause);
 
     @Override
     public void handleMessage(Message msg) {
@@ -504,7 +517,7 @@
                 break;
 
             case EVENT_CLEAN_UP_ALL_CONNECTIONS: {
-                onCleanUpAllConnections();
+                onCleanUpAllConnections((String) msg.obj);
                 break;
             }
             case EVENT_CLEAN_UP_CONNECTION: {
@@ -546,16 +559,6 @@
         return result;
     }
 
-    protected abstract void startNetStatPoll();
-
-    protected abstract void stopNetStatPoll();
-
-    protected abstract void restartRadio();
-
-    protected abstract void log(String s);
-
-    protected abstract void loge(String s);
-
     protected int apnTypeToId(String type) {
         if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT)) {
             return APN_DEFAULT_ID;
@@ -602,12 +605,6 @@
         }
     }
 
-    protected abstract boolean isApnTypeAvailable(String type);
-
-    protected abstract void setState(State s);
-
-    protected abstract void gotoIdleAndNotifyDataConnection(String reason);
-
     protected LinkProperties getLinkProperties(String apnType) {
         int id = apnTypeToId(apnType);
         if (isApnIdEnabled(id)) {
@@ -728,13 +725,12 @@
         return possible;
     }
 
-    protected abstract boolean isDataAllowed();
-
     public boolean isApnTypeEnabled(String apnType) {
         if (apnType == null) {
-            apnType = getActiveApnType();
+            return false;
+        } else {
+            return isApnIdEnabled(apnTypeToId(apnType));
         }
-        return isApnIdEnabled(apnTypeToId(apnType));
     }
 
     protected synchronized boolean isApnIdEnabled(int id) {
@@ -924,7 +920,7 @@
                     resetAllRetryCounts();
                     onTrySetupData(Phone.REASON_DATA_ENABLED);
                 } else {
-                    cleanUpAllConnections();
+                    cleanUpAllConnections(null);
                 }
             }
         }
@@ -934,8 +930,9 @@
         return mDataEnabled;
     }
 
-    public void cleanUpAllConnections() {
+    public void cleanUpAllConnections(String cause) {
         Message msg = obtainMessage(EVENT_CLEAN_UP_ALL_CONNECTIONS);
+        msg.obj = cause;
         sendMessage(msg);
     }
 
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 5408ce9..779b125 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -243,7 +243,7 @@
         synchronized(PhoneProxy.lockForRadioTechnologyChange) {
             mCM.unSetOnCallRing(this);
             // Must cleanup all connectionS and needs to use sendMessage!
-            mDataConnection.cleanUpAllConnections();
+            mDataConnection.cleanUpAllConnections(null);
             mIsTheCurrentActivePhone = false;
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 8c9fb79..4faf297 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -66,8 +66,11 @@
     int NETWORK_MODE_EVDO_NO_CDMA   = 6; /* EvDo only */
     int NETWORK_MODE_GLOBAL         = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL)
                                             AVAILABLE Application Settings menu*/
-    int NETWORK_MODE_LTE_ONLY       = 8; /* LTE Only mode. Used only for testing purposes.Not
-                                            user selectable from regular UI */
+    int NETWORK_MODE_LTE_CDMA_EVDO  = 8; /* LTE, CDMA and EvDo */
+    int NETWORK_MODE_LTE_GSM_WCDMA  = 9; /* LTE, GSM/WCDMA */
+    int NETWORK_MODE_LTE_CMDA_EVDO_GSM_WCDMA = 10; /* LTE, CDMA, EvDo, GSM/WCDMA */
+    int NETWORK_MODE_LTE_ONLY       = 11; /* LTE Only mode. */
+
     int PREFERRED_NETWORK_MODE      = NETWORK_MODE_WCDMA_PREF;
 
     /* CDMA subscription source. See ril.h RIL_REQUEST_CDMA_SET_SUBSCRIPTION */
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index 89d7174..8fd6de1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -122,13 +122,7 @@
         } else if (mDataConnection.isApnTypeEnabled(apnType) == false) {
             ret = DataState.DISCONNECTED;
         } else {
-            DataConnectionTracker.State state;
-            if (isCdmaDataConnectionTracker) {
-                state = mDataConnection.getState();
-            } else {
-                state = ((GsmDataConnectionTracker)mDataConnection).getState(apnType);
-            }
-            switch (state) {
+            switch (mDataConnection.getState(apnType)) {
                 case FAILED:
                 case IDLE:
                     ret = DataState.DISCONNECTED;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index dd5091c..31abe34 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -633,7 +633,7 @@
                 mDataConnection.isApnTypeActive(apnType) == false) {
             ret = DataState.DISCONNECTED;
         } else {
-            switch (mDataConnection.getState()) {
+            switch (mDataConnection.getState(apnType)) {
                 case FAILED:
                 case IDLE:
                     ret = DataState.DISCONNECTED;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index b24909f..3d10c9c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -160,6 +160,11 @@
     }
 
     @Override
+    public synchronized State getState(String apnType) {
+        return mState;
+    }
+
+    @Override
     protected boolean isApnTypeAvailable(String type) {
         for (String s : mSupportedApnTypes) {
             if (TextUtils.equals(type, s)) {
@@ -264,7 +269,7 @@
             if(conn != null) {
                 if (tearDown) {
                     if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
-                    conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE,
+                    conn.disconnect(reason, obtainMessage(EVENT_DISCONNECT_DONE,
                             conn.getDataConnectionId(), 0, reason));
                     notificationDeferred = true;
                 } else {
@@ -362,7 +367,7 @@
     protected void restartRadio() {
         if (DBG) log("Cleanup connection and wait " +
                 (TIME_DELAYED_TO_RESTART_RADIO / 1000) + "s to restart radio");
-        cleanUpAllConnections();
+        cleanUpAllConnections(null);
         sendEmptyMessageDelayed(EVENT_RESTART_RADIO, TIME_DELAYED_TO_RESTART_RADIO);
         mPendingRestartRadio = true;
     }
@@ -519,14 +524,14 @@
 
     protected void onRecordsLoaded() {
         if (mState == State.FAILED) {
-            cleanUpAllConnections();
+            cleanUpAllConnections(null);
         }
         sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED));
     }
 
     protected void onNVReady() {
         if (mState == State.FAILED) {
-            cleanUpAllConnections();
+            cleanUpAllConnections(null);
         }
         sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
     }
@@ -565,7 +570,7 @@
             trySetupData(Phone.REASON_ROAMING_ON);
         } else {
             if (DBG) log("Tear down data connection on roaming.");
-            cleanUpAllConnections();
+            cleanUpAllConnections(null);
         }
     }
 
@@ -586,7 +591,7 @@
         notifyDataAvailability(null);
 
         if (mState != State.IDLE) {
-            cleanUpAllConnections();
+            cleanUpAllConnections(null);
         }
     }
 
@@ -603,7 +608,7 @@
             log("We're on the simulator; assuming radio off is meaningless");
         } else {
             if (DBG) log("Radio is off and clean up all connection");
-            cleanUpAllConnections();
+            cleanUpAllConnections(null);
         }
     }
 
@@ -709,9 +714,9 @@
     }
 
     @Override
-    protected void onCleanUpAllConnections() {
+    protected void onCleanUpAllConnections(String cause) {
         // Only one CDMA connection is supported
-        cleanUpConnection(true, null);
+        cleanUpConnection(true, cause);
     }
 
     private void createAllDataConnectionList() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index 81b4077def..7ca3bea 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -152,17 +152,6 @@
                 pollStateDone();
                 break;
 
-            case SIM_NOT_READY:
-            case SIM_LOCKED_OR_ABSENT:
-            case SIM_READY:
-                log("Radio Technology Change ongoing, setting SS to off");
-                newSS.setStateOff();
-                newCellLoc.setStateInvalid();
-                setSignalStrengthDefaultValues();
-                mGotCountryCode = false;
-                // NOTE: pollStateDone() is not needed in this case
-                break;
-
             default:
                 // Issue all poll-related commands at once, then count
                 // down the responses which are allowed to arrive
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 658de84..4c6cd17 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -525,7 +525,7 @@
         synchronized (this) {
             if (!mPendingRadioPowerOffAfterDataOff) {
                 if (dcTracker.isAnyActiveDataConnections()) {
-                    dcTracker.cleanUpAllConnections();
+                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
                     if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
                         if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
                         mPendingRadioPowerOffAfterDataOff = true;
@@ -534,7 +534,7 @@
                         hangupAndPowerOff();
                     }
                 } else {
-                    dcTracker.cleanUpAllConnections();
+                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
                     if (DBG) log("Data disconnected, turn off radio right away.");
                     hangupAndPowerOff();
                 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 261a61a..36094a1 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -312,7 +312,7 @@
                 mDataConnection.isApnTypeActive(apnType) == false) {
             ret = DataState.DISCONNECTED;
         } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
-            switch (mDataConnection.getState()) {
+            switch (mDataConnection.getState(apnType)) {
                 case FAILED:
                 case IDLE:
                     ret = DataState.DISCONNECTED;
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index d8ff268..4d94b27 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -102,26 +102,6 @@
     //useful for debugging
     boolean mFailNextConnect = false;
 
-    /**
-     * allApns holds all apns for this sim spn, retrieved from
-     * the Carrier DB.
-     *
-     * Create once after simcard info is loaded
-     */
-    private ArrayList<ApnSetting> mAllApns = null;
-
-    private ApnSetting mPreferredApn = null;
-
-    /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
-    private HashMap<String, Integer> mApnToDataConnectionId =
-                                    new HashMap<String, Integer>();
-
-    /** Phone.APN_TYPE_* ===> ApnContext */
-    private ConcurrentHashMap<String, ApnContext> mApnContexts;
-
-    /** Is packet service restricted by network */
-    private boolean mIsPsRestricted = false;
-
     //***** Constants
 
     private static final int POLL_PDP_MILLIS = 5 * 1000;
@@ -320,7 +300,13 @@
         return null;
     }
 
+    @Override
+    protected void setState(State s) {
+        if (DBG) log("setState should not be used in GSM" + s);
+    }
+
     // Return state of specific apn type
+    @Override
     public synchronized State getState(String apnType) {
         ApnContext apnContext = mApnContexts.get(apnType);
         if (apnContext != null) {
@@ -613,11 +599,6 @@
 
     }
 
-    @Override
-    protected void setState(State s) {
-        if (DBG) log("setState should not be used in GSM" + s);
-    }
-
     private boolean trySetupData(ApnContext apnContext) {
 
         if (DBG)
@@ -727,8 +708,8 @@
      */
 
     @Override
-    protected void onCleanUpAllConnections() {
-        cleanUpAllConnections(true, null);
+    protected void onCleanUpAllConnections(String cause) {
+        cleanUpAllConnections(true, cause);
     }
 
     private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
@@ -764,7 +745,7 @@
             apnContext.setState(State.DISCONNECTING);
             if (tearDown ) {
                 Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
-                conn.disconnect(msg);
+                conn.disconnect(apnContext.getReason(), msg);
             } else {
                 conn.resetSynchronously();
                 apnContext.setState(State.IDLE);
diff --git a/tests/BiDiTests/res/layout/biditest_main.xml b/tests/BiDiTests/res/layout/biditest_main.xml
index 9f77ad2..087c9a3 100644
--- a/tests/BiDiTests/res/layout/biditest_main.xml
+++ b/tests/BiDiTests/res/layout/biditest_main.xml
@@ -48,6 +48,11 @@
 
     </LinearLayout>
 
+    <SeekBar android:id="@+id/seekbar"
+               android:layout_height="wrap_content"
+               android:layout_width="match_parent"
+               />
+
     <view class="com.android.bidi.BiDiTestView"
         android:id="@+id/main"
         android:layout_width="match_parent"
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index ecff76e..632a02e 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -18,9 +18,11 @@
     <string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
     <string name="normal_text">Normal String</string>
     <string name="normal_long_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
+    <string name="normal_long_text_2">nnnnnnnnnnnnnnnnnnnnnnnn</string>
+    <string name="normal_long_text_3">Notify me when an open network is available</string>
     <string name="arabic_text">&#x0644;&#x0627;</string>
     <string name="chinese_text">利比亚局势或影响美俄关系发展</string>
     <string name="italic_text">Italic String</string>
-    <string name="bold_text">Bold String</string>
+    <string name="bold_text">Bold String - other text</string>
     <string name="bold_italic_text">Bold Italic String</string>
 </resources>
\ No newline at end of file
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index 3d7dd81..6c71574 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -20,16 +20,44 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
+import android.widget.SeekBar;
+
+import static com.android.bidi.BiDiTestConstants.FONT_MIN_SIZE;
+import static com.android.bidi.BiDiTestConstants.FONT_MAX_SIZE;
 
 public class BiDiTestActivity extends Activity {
 
     static final String TAG = "BiDiTestActivity";
 
+    static final int INIT_TEXT_SIZE = (FONT_MAX_SIZE - FONT_MIN_SIZE) / 2;
+
+    private BiDiTestView textView;
+    private SeekBar textSizeSeekBar;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.biditest_main);
+
+        textView = (BiDiTestView) findViewById(R.id.main);
+        textView.setCurrentTextSize(INIT_TEXT_SIZE);
+
+        textSizeSeekBar = (SeekBar) findViewById(R.id.seekbar);
+        textSizeSeekBar.setProgress(INIT_TEXT_SIZE);
+        textSizeSeekBar.setMax(FONT_MAX_SIZE - FONT_MIN_SIZE);
+
+        textSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                textView.setCurrentTextSize(FONT_MIN_SIZE + progress);
+            }
+
+            public void onStartTrackingTouch(SeekBar seekBar) {
+            }
+
+            public void onStopTrackingTouch(SeekBar seekBar) {
+            }
+        });
     }
 
     @Override
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestConstants.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestConstants.java
new file mode 100644
index 0000000..5c28e3d
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestConstants.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bidi;
+
+public class BiDiTestConstants {
+    public static final int FONT_MIN_SIZE = 8;
+    public static final int FONT_MAX_SIZE = 72;
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
index e9b6fa6..cd415c2 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
@@ -32,9 +32,8 @@
 
     private static final int BORDER_PADDING = 4;
     private static final int TEXT_PADDING = 16;
-    private static final int TEXT_SIZE = 32;
-    private static final int ORIGIN = 48;
-    private static final int DELTA_Y = TEXT_SIZE;
+    private static final int TEXT_SIZE = 16;
+    private static final int ORIGIN = 80;
 
     private static final float DEFAULT_ITALIC_SKEW_X = -0.25f;
 
@@ -43,6 +42,8 @@
 
     private String NORMAL_TEXT;
     private String NORMAL_LONG_TEXT;
+    private String NORMAL_LONG_TEXT_2;
+    private String NORMAL_LONG_TEXT_3;
     private String ITALIC_TEXT;
     private String BOLD_TEXT;
     private String BOLD_ITALIC_TEXT;
@@ -51,6 +52,8 @@
 
     private Typeface typeface;
 
+    private int currentTextSize;
+
     public BiDiTestView(Context context) {
         super(context);
         init(context);
@@ -69,6 +72,8 @@
     private void init(Context context) {
         NORMAL_TEXT = context.getString(R.string.normal_text);
         NORMAL_LONG_TEXT = context.getString(R.string.normal_long_text);
+        NORMAL_LONG_TEXT_2 = context.getString(R.string.normal_long_text_2);
+        NORMAL_LONG_TEXT_3 = context.getString(R.string.normal_long_text_3);
         ITALIC_TEXT = context.getString(R.string.italic_text);
         BOLD_TEXT = context.getString(R.string.bold_text);
         BOLD_ITALIC_TEXT = context.getString(R.string.bold_italic_text);
@@ -79,34 +84,50 @@
         paint.setAntiAlias(true);
     }
 
+    public void setCurrentTextSize(int size) {
+        currentTextSize = size;
+        invalidate();
+    }
+
     @Override
     public void onDraw(Canvas canvas) {
         drawInsideRect(canvas, Color.BLACK);
 
-        int deltaX = testString(canvas, NORMAL_TEXT, ORIGIN, ORIGIN, paint, typeface,
-                false, false,  Paint.DIRECTION_LTR);
-        deltaX += testString(canvas, ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, paint, typeface,
-                true, false,  Paint.DIRECTION_LTR);
-        deltaX += testString(canvas, BOLD_TEXT, ORIGIN + deltaX, ORIGIN, paint, typeface,
-                false, true,  Paint.DIRECTION_LTR);
-        deltaX += testString(canvas, BOLD_ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, paint, typeface,
-                true, true,  Paint.DIRECTION_LTR);
+        int deltaX = testString(canvas, NORMAL_TEXT, ORIGIN, ORIGIN,
+                paint, typeface, false, false,  Paint.DIRECTION_LTR, currentTextSize);
+
+        deltaX += testString(canvas, ITALIC_TEXT, ORIGIN + deltaX, ORIGIN,
+                paint, typeface, true, false,  Paint.DIRECTION_LTR, currentTextSize);
+
+        deltaX += testString(canvas, BOLD_TEXT, ORIGIN + deltaX, ORIGIN,
+                paint, typeface, false, true,  Paint.DIRECTION_LTR, currentTextSize);
+
+        deltaX += testString(canvas, BOLD_ITALIC_TEXT, ORIGIN + deltaX, ORIGIN,
+                paint, typeface, true, true,  Paint.DIRECTION_LTR, currentTextSize);
 
         // Test with a long string
-        deltaX = testString(canvas, NORMAL_LONG_TEXT, ORIGIN, ORIGIN + 2 * DELTA_Y, paint, typeface,
-                false, false,  Paint.DIRECTION_LTR);
+        deltaX = testString(canvas, NORMAL_LONG_TEXT, ORIGIN, ORIGIN + 2 * currentTextSize,
+                paint, typeface, false, false,  Paint.DIRECTION_LTR, currentTextSize);
+
+        // Test with a long string
+        deltaX = testString(canvas, NORMAL_LONG_TEXT_2, ORIGIN, ORIGIN + 4 * currentTextSize,
+                paint, typeface, false, false,  Paint.DIRECTION_LTR, currentTextSize);
+
+        // Test with a long string
+        deltaX = testString(canvas, NORMAL_LONG_TEXT_3, ORIGIN, ORIGIN + 6 * currentTextSize,
+                paint, typeface, false, false,  Paint.DIRECTION_LTR, currentTextSize);
 
         // Test Arabic ligature
-        deltaX = testString(canvas, ARABIC_TEXT, ORIGIN, ORIGIN + 4 * DELTA_Y, paint, typeface,
-                false, false,  Paint.DIRECTION_RTL);
+        deltaX = testString(canvas, ARABIC_TEXT, ORIGIN, ORIGIN + 8 * currentTextSize,
+                paint, typeface, false, false,  Paint.DIRECTION_RTL, currentTextSize);
 
         // Test Chinese
-        deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 6 * DELTA_Y, paint, typeface,
-                false, false,  Paint.DIRECTION_LTR);
+        deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 10 * currentTextSize,
+                paint, typeface, false, false,  Paint.DIRECTION_LTR, currentTextSize);
     }
 
     private int testString(Canvas canvas, String text, int x, int y, Paint paint, Typeface typeface,
-            boolean isItalic, boolean isBold, int dir) {
+            boolean isItalic, boolean isBold, int dir, int textSize) {
         paint.setTypeface(typeface);
 
         // Set paint properties
@@ -118,27 +139,28 @@
             paint.setTextSkewX(DEFAULT_ITALIC_SKEW_X);
         }
 
-        drawTextWithCanvasDrawText(text, canvas, x, y, TEXT_SIZE, Color.WHITE);
+        drawTextWithCanvasDrawText(text, canvas, x, y, textSize, Color.WHITE);
 
         int length = text.length();
         float[] advances = new float[length];
-        float textWidth = paint.getTextRunAdvances(text, 0, length, 0, length, 0, advances, 0);
+        float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, 0, advances, 0);
+        float textWidthICU = paint.getTextRunAdvancesICU(text, 0, length, 0, length, 0, advances, 0);
 
-        logAdvances(text, textWidth, advances);
-        drawBoxAroundText(canvas, x, y, textWidth, TEXT_SIZE, Color.RED);
+        logAdvances(text, textWidthHB, textWidthICU, advances);
+        drawMetricsAroundText(canvas, x, y, textWidthHB, textWidthICU, textSize, Color.RED, Color.GREEN);
 
         paint.setColor(Color.WHITE);
         char[] glyphs = new char[2*length];
         int count = getGlyphs(text, glyphs, dir);
 
-        logGlypths(glyphs, count);
-        drawTextWithDrawGlyph(canvas, glyphs, count, x, y + DELTA_Y);
+//        logGlypths(glyphs, count);
+        drawTextWithDrawGlyph(canvas, glyphs, count, x, y + currentTextSize);
 
         // Restore old paint properties
         paint.setFakeBoldText(oldFakeBold);
         paint.setTextSkewX(oldTextSkewX);
 
-        return (int) Math.ceil(textWidth) + TEXT_PADDING;
+        return (int) Math.ceil(textWidthHB) + TEXT_PADDING;
     }
 
     private void drawTextWithDrawGlyph(Canvas canvas, char[] glyphs, int count, int x, int y) {
@@ -172,19 +194,21 @@
         canvas.drawText(text, x, y, paint);
     }
 
-    private void drawBoxAroundText(Canvas canvas, int x, int y, float textWidth, int textSize,
-            int color) {
+    private void drawMetricsAroundText(Canvas canvas, int x, int y, float textWidthHB,
+            float textWidthICU, int textSize, int color, int colorICU) {
         paint.setColor(color);
         canvas.drawLine(x, y - textSize, x, y + 8, paint);
-        canvas.drawLine(x, y + 8, x + textWidth, y + 8, paint);
-        canvas.drawLine(x + textWidth, y - textSize, x + textWidth, y + 8, paint);
+        canvas.drawLine(x, y + 8, x + textWidthHB, y + 8, paint);
+        canvas.drawLine(x + textWidthHB, y - textSize, x + textWidthHB, y + 8, paint);
+        paint.setColor(colorICU);
+        canvas.drawLine(x + textWidthICU, y - textSize, x + textWidthICU, y + 8, paint);
     }
 
-    private void logAdvances(String text, float textWidth, float[] advances) {
-        Log.v(TAG, "Advances for text: " + text + " total=" + textWidth);
-        int length = advances.length;
-        for(int n=0; n<length; n++){
-            Log.v(TAG, "adv[" + n + "]=" + advances[n]);
-        }
+    private void logAdvances(String text, float textWidth, float textWidthICU, float[] advances) {
+        Log.v(TAG, "Advances for text: " + text + " total= " + textWidth + " - totalICU= " + textWidthICU);
+//        int length = advances.length;
+//        for(int n=0; n<length; n++){
+//            Log.v(TAG, "adv[" + n + "]=" + advances[n]);
+//        }
     }
 }
diff --git a/tests/CoreTests/android/core/HttpHeaderTest.java b/tests/CoreTests/android/core/HttpHeaderTest.java
index a5d48578..eedbc3f 100644
--- a/tests/CoreTests/android/core/HttpHeaderTest.java
+++ b/tests/CoreTests/android/core/HttpHeaderTest.java
@@ -19,12 +19,19 @@
 import org.apache.http.util.CharArrayBuffer;
 
 import android.net.http.Headers;
+import android.util.Log;
+import android.webkit.CacheManager;
+import android.webkit.CacheManager.CacheResult;
+
+import java.lang.reflect.Method;
 
 public class HttpHeaderTest extends AndroidTestCase {
 
     static final String LAST_MODIFIED = "Last-Modified: Fri, 18 Jun 2010 09:56:47 GMT";
     static final String CACHE_CONTROL_MAX_AGE = "Cache-Control:max-age=15";
     static final String CACHE_CONTROL_PRIVATE = "Cache-Control: private";
+    static final String CACHE_CONTROL_COMPOUND = "Cache-Control: no-cache, max-age=200000";
+    static final String CACHE_CONTROL_COMPOUND2 = "Cache-Control: max-age=200000, no-cache";
 
     /**
      * Tests that cache control header supports multiple instances of the header,
@@ -59,4 +66,39 @@
         h.parseHeader(buffer);
         assertEquals("max-age=15,private", h.getCacheControl());
     }
+
+    // Test that cache behaves correctly when receiving a compund
+    // cache-control statement containing no-cache and max-age argument.
+    //
+    // If a cache control header contains both a max-age arument and
+    // a no-cache argument the max-age argument should be ignored.
+    // The resource can be cached, but a validity check must be done on
+    // every request. Test case checks that the expiry time is 0 for
+    // this item, so item will be validated on subsequent requests.
+    public void testCacheControlMultipleArguments() throws Exception {
+        // get private method CacheManager.parseHeaders()
+        Method m = CacheManager.class.getDeclaredMethod("parseHeaders",
+                new Class[] {int.class, Headers.class, String.class});
+        m.setAccessible(true);
+
+        // create indata
+        Headers h = new Headers();
+        CharArrayBuffer buffer = new CharArrayBuffer(64);
+        buffer.append(CACHE_CONTROL_COMPOUND);
+        h.parseHeader(buffer);
+
+        CacheResult c = (CacheResult)m.invoke(null, 200, h, "text/html");
+
+        // Check that expires is set to 0, to ensure that no-cache has overridden
+        // the max-age argument
+        assertEquals(0, c.getExpires());
+
+        // check reverse order
+        buffer.clear();
+        buffer.append(CACHE_CONTROL_COMPOUND2);
+        h.parseHeader(buffer);
+
+        c = (CacheResult)m.invoke(null, 200, h, "text/html");
+        assertEquals(0, c.getExpires());
+    }
 }