Merge "Fix 6507787: fix MMI PUK unlock procedure" into jb-dev
diff --git a/Android.mk b/Android.mk
index b0a3dac..5aaea97 100644
--- a/Android.mk
+++ b/Android.mk
@@ -193,6 +193,7 @@
 	location/java/android/location/INetInitiatedListener.aidl \
 	media/java/android/media/IAudioService.aidl \
 	media/java/android/media/IAudioFocusDispatcher.aidl \
+    media/java/android/media/IAudioRoutesObserver.aidl \
 	media/java/android/media/IMediaScannerListener.aidl \
 	media/java/android/media/IMediaScannerService.aidl \
 	media/java/android/media/IRemoteControlClient.aidl \
diff --git a/api/16.txt b/api/16.txt
index eb09d85..a1ba90c 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -3679,6 +3679,7 @@
   public class MediaRouteActionProvider extends android.view.ActionProvider {
     ctor public MediaRouteActionProvider(android.content.Context);
     method public android.view.View onCreateActionView();
+    method public void setExtendedSettingsClickListener(android.view.View.OnClickListener);
     method public void setRouteTypes(int);
   }
 
@@ -3687,7 +3688,9 @@
     ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet);
     ctor public MediaRouteButton(android.content.Context, android.util.AttributeSet, int);
     method public int getRouteTypes();
+    method public void setExtendedSettingsClickListener(android.view.View.OnClickListener);
     method public void setRouteTypes(int);
+    method public void showDialog();
   }
 
   public class NativeActivity extends android.app.Activity implements android.view.InputQueue.Callback android.view.SurfaceHolder.Callback2 android.view.ViewTreeObserver.OnGlobalLayoutListener {
@@ -11511,6 +11514,7 @@
     method public void addUserRoute(android.media.MediaRouter.UserRouteInfo);
     method public void clearUserRoutes();
     method public android.media.MediaRouter.RouteCategory createRouteCategory(java.lang.CharSequence, boolean);
+    method public android.media.MediaRouter.RouteCategory createRouteCategory(int, boolean);
     method public android.media.MediaRouter.UserRouteInfo createUserRoute(android.media.MediaRouter.RouteCategory);
     method public android.media.MediaRouter.RouteCategory getCategoryAt(int);
     method public int getCategoryCount();
@@ -11524,7 +11528,8 @@
     field public static final int ROUTE_TYPE_USER = 8388608; // 0x800000
   }
 
-  public static abstract interface MediaRouter.Callback {
+  public static abstract class MediaRouter.Callback {
+    ctor public MediaRouter.Callback();
     method public abstract void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteGrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup, int);
@@ -11536,6 +11541,7 @@
 
   public static class MediaRouter.RouteCategory {
     method public java.lang.CharSequence getName();
+    method public java.lang.CharSequence getName(android.content.Context);
     method public java.util.List<android.media.MediaRouter.RouteInfo> getRoutes(java.util.List<android.media.MediaRouter.RouteInfo>);
     method public int getSupportedTypes();
     method public boolean isGroupable();
@@ -11548,17 +11554,21 @@
     method public int getRouteCount();
     method public void removeRoute(android.media.MediaRouter.RouteInfo);
     method public void removeRoute(int);
+    method public void setIconDrawable(android.graphics.drawable.Drawable);
+    method public void setIconResource(int);
   }
 
   public static class MediaRouter.RouteInfo {
     method public android.media.MediaRouter.RouteCategory getCategory();
     method public android.media.MediaRouter.RouteGroup getGroup();
+    method public android.graphics.drawable.Drawable getIconDrawable();
     method public java.lang.CharSequence getName();
+    method public java.lang.CharSequence getName(android.content.Context);
     method public java.lang.CharSequence getStatus();
     method public int getSupportedTypes();
   }
 
-  public static class MediaRouter.SimpleCallback implements android.media.MediaRouter.Callback {
+  public static class MediaRouter.SimpleCallback extends android.media.MediaRouter.Callback {
     ctor public MediaRouter.SimpleCallback();
     method public void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
@@ -11570,8 +11580,14 @@
   }
 
   public static class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo {
+    method public java.lang.Object getTag();
+    method public void setIconDrawable(android.graphics.drawable.Drawable);
+    method public void setIconResource(int);
     method public void setName(java.lang.CharSequence);
+    method public void setName(int);
+    method public void setRemoteControlClient(android.media.RemoteControlClient);
     method public void setStatus(java.lang.CharSequence);
+    method public void setTag(java.lang.Object);
   }
 
   public class MediaScannerConnection implements android.content.ServiceConnection {
@@ -24358,12 +24374,6 @@
     method protected boolean verifyDrawable(android.graphics.drawable.Drawable);
     method public boolean willNotCacheDrawing();
     method public boolean willNotDraw();
-    field public static final int ACCESSIBILITY_FOCUS_BACKWARD = 4097; // 0x1001
-    field public static final int ACCESSIBILITY_FOCUS_DOWN = 4226; // 0x1082
-    field public static final int ACCESSIBILITY_FOCUS_FORWARD = 4098; // 0x1002
-    field public static final int ACCESSIBILITY_FOCUS_LEFT = 4113; // 0x1011
-    field public static final int ACCESSIBILITY_FOCUS_RIGHT = 4162; // 0x1042
-    field public static final int ACCESSIBILITY_FOCUS_UP = 4129; // 0x1021
     field public static final android.util.Property ALPHA;
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
@@ -24385,7 +24395,6 @@
     field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     field protected static final int[] FOCUSED_STATE_SET;
     field protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET;
-    field public static final int FOCUS_ACCESSIBILITY = 4096; // 0x1000
     field public static final int FOCUS_BACKWARD = 1; // 0x1
     field public static final int FOCUS_DOWN = 130; // 0x82
     field public static final int FOCUS_FORWARD = 2; // 0x2
@@ -25332,7 +25341,6 @@
 
   public abstract class AccessibilityNodeProvider {
     ctor public AccessibilityNodeProvider();
-    method public android.view.accessibility.AccessibilityNodeInfo accessibilityFocusSearch(int, int);
     method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
     method public android.view.accessibility.AccessibilityNodeInfo findAccessibilityFocus(int);
     method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
diff --git a/api/current.txt b/api/current.txt
index a62698e..387bb65 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11514,6 +11514,7 @@
     method public void addUserRoute(android.media.MediaRouter.UserRouteInfo);
     method public void clearUserRoutes();
     method public android.media.MediaRouter.RouteCategory createRouteCategory(java.lang.CharSequence, boolean);
+    method public android.media.MediaRouter.RouteCategory createRouteCategory(int, boolean);
     method public android.media.MediaRouter.UserRouteInfo createUserRoute(android.media.MediaRouter.RouteCategory);
     method public android.media.MediaRouter.RouteCategory getCategoryAt(int);
     method public int getCategoryCount();
@@ -11527,7 +11528,8 @@
     field public static final int ROUTE_TYPE_USER = 8388608; // 0x800000
   }
 
-  public static abstract interface MediaRouter.Callback {
+  public static abstract class MediaRouter.Callback {
+    ctor public MediaRouter.Callback();
     method public abstract void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public abstract void onRouteGrouped(android.media.MediaRouter, android.media.MediaRouter.RouteInfo, android.media.MediaRouter.RouteGroup, int);
@@ -11539,6 +11541,7 @@
 
   public static class MediaRouter.RouteCategory {
     method public java.lang.CharSequence getName();
+    method public java.lang.CharSequence getName(android.content.Context);
     method public java.util.List<android.media.MediaRouter.RouteInfo> getRoutes(java.util.List<android.media.MediaRouter.RouteInfo>);
     method public int getSupportedTypes();
     method public boolean isGroupable();
@@ -11560,11 +11563,14 @@
     method public android.media.MediaRouter.RouteGroup getGroup();
     method public android.graphics.drawable.Drawable getIconDrawable();
     method public java.lang.CharSequence getName();
+    method public java.lang.CharSequence getName(android.content.Context);
     method public java.lang.CharSequence getStatus();
     method public int getSupportedTypes();
+    method public java.lang.Object getTag();
+    method public void setTag(java.lang.Object);
   }
 
-  public static class MediaRouter.SimpleCallback implements android.media.MediaRouter.Callback {
+  public static class MediaRouter.SimpleCallback extends android.media.MediaRouter.Callback {
     ctor public MediaRouter.SimpleCallback();
     method public void onRouteAdded(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
     method public void onRouteChanged(android.media.MediaRouter, android.media.MediaRouter.RouteInfo);
@@ -11576,13 +11582,12 @@
   }
 
   public static class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo {
-    method public java.lang.Object getTag();
     method public void setIconDrawable(android.graphics.drawable.Drawable);
     method public void setIconResource(int);
     method public void setName(java.lang.CharSequence);
+    method public void setName(int);
     method public void setRemoteControlClient(android.media.RemoteControlClient);
     method public void setStatus(java.lang.CharSequence);
-    method public void setTag(java.lang.Object);
   }
 
   public class MediaScannerConnection implements android.content.ServiceConnection {
@@ -24369,12 +24374,6 @@
     method protected boolean verifyDrawable(android.graphics.drawable.Drawable);
     method public boolean willNotCacheDrawing();
     method public boolean willNotDraw();
-    field public static final int ACCESSIBILITY_FOCUS_BACKWARD = 4097; // 0x1001
-    field public static final int ACCESSIBILITY_FOCUS_DOWN = 4226; // 0x1082
-    field public static final int ACCESSIBILITY_FOCUS_FORWARD = 4098; // 0x1002
-    field public static final int ACCESSIBILITY_FOCUS_LEFT = 4113; // 0x1011
-    field public static final int ACCESSIBILITY_FOCUS_RIGHT = 4162; // 0x1042
-    field public static final int ACCESSIBILITY_FOCUS_UP = 4129; // 0x1021
     field public static final android.util.Property ALPHA;
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
@@ -24396,7 +24395,6 @@
     field protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     field protected static final int[] FOCUSED_STATE_SET;
     field protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET;
-    field public static final int FOCUS_ACCESSIBILITY = 4096; // 0x1000
     field public static final int FOCUS_BACKWARD = 1; // 0x1
     field public static final int FOCUS_DOWN = 130; // 0x82
     field public static final int FOCUS_FORWARD = 2; // 0x2
@@ -25343,7 +25341,6 @@
 
   public abstract class AccessibilityNodeProvider {
     ctor public AccessibilityNodeProvider();
-    method public android.view.accessibility.AccessibilityNodeInfo accessibilityFocusSearch(int, int);
     method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
     method public android.view.accessibility.AccessibilityNodeInfo findAccessibilityFocus(int);
     method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 0bc7371..96a6438 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -53,11 +53,7 @@
         unlink(pkgdir);
         return -errno;
     }
-    if (chown(pkgdir, uid, gid) < 0) {
-        ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
-        unlink(pkgdir);
-        return -errno;
-    }
+
     if (mkdir(libdir, 0755) < 0) {
         ALOGE("cannot create dir '%s': %s\n", libdir, strerror(errno));
         unlink(pkgdir);
@@ -75,6 +71,13 @@
         unlink(pkgdir);
         return -errno;
     }
+
+    if (chown(pkgdir, uid, gid) < 0) {
+        ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
+        unlink(libdir);
+        unlink(pkgdir);
+        return -errno;
+    }
     return 0;
 }
 
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index a4eebda..018b25d 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -103,7 +103,7 @@
 
         if (mToggleMode) {
             if (mRemoteActive) {
-                mRouter.selectRoute(mRouteTypes, mRouter.getSystemAudioRoute());
+                mRouter.selectRouteInt(mRouteTypes, mRouter.getSystemAudioRoute());
             } else {
                 final int N = mRouter.getRouteCount();
                 for (int i = 0; i < N; i++) {
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 78dc86f..4d1b836 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -79,6 +79,16 @@
     // be dequeued.
     private static final long DEFAULT_FRAME_DELAY = 10;
 
+    // The fake vsync delay in milliseconds.
+    // When the screen is off, we might not receive real vsync pulses from the hardware
+    // which would cause posted Choreographer callbacks to not run.  This is bad because
+    // messages in the Looper might be blocked behind a barrier that is scheduled to be
+    // removed by one of those Choreographer callback (see ViewRootImpl.doTraversals).
+    // Until the barrier is removed, those messages will not run.  To prevent starvation
+    // of the Looper, we synthesize fake vsync pulses at a reduced rate whenever the
+    // display hardware stops generating them.
+    private static final long FAKE_VSYNC_DELAY = 100;
+
     // The number of milliseconds between animation frames.
     private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;
 
@@ -113,6 +123,7 @@
     private static final int MSG_DO_FRAME = 0;
     private static final int MSG_DO_SCHEDULE_VSYNC = 1;
     private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
+    private static final int MSG_FAKE_VSYNC = 3;
 
     // All frame callbacks posted by applications have this token.
     private static final Object FRAME_CALLBACK_TOKEN = new Object() {
@@ -587,6 +598,13 @@
 
     private void scheduleVsyncLocked() {
         mDisplayEventReceiver.scheduleVsync();
+
+        // Post a message to simulate a fake vsync pulse at a reduced rate in case the
+        // display hardware stops generating them.  This ensures that Choreographer
+        // callbacks can continue to run even if the screen is off.
+        Message msg = mHandler.obtainMessage(MSG_FAKE_VSYNC);
+        msg.setAsynchronous(true);
+        mHandler.sendMessageDelayed(msg, FAKE_VSYNC_DELAY);
     }
 
     private boolean isRunningOnLooperThreadLocked() {
@@ -662,6 +680,12 @@
                 case MSG_DO_SCHEDULE_CALLBACK:
                     doScheduleCallback(msg.arg1);
                     break;
+                case MSG_FAKE_VSYNC:
+                    if (DEBUG) {
+                        Log.d(TAG, "Handling fake vsync while screen is off.");
+                    }
+                    doFrame(System.nanoTime(), 0);
+                    break;
             }
         }
     }
@@ -683,6 +707,17 @@
             // the message queue.  If there are no messages in the queue with timestamps
             // earlier than the frame time, then the vsync event will be processed immediately.
             // Otherwise, messages that predate the vsync event will be handled first.
+            if (mHavePendingVsync) {
+                if (DEBUG) {
+                    Log.d(TAG, "Already have a pending vsync event.  There should only be "
+                            + "one at a time but they can double up when a fake vsync "
+                            + "is handled in place of a real one.");
+                }
+                mHandler.removeCallbacks(this);
+            } else {
+                mHavePendingVsync = true;
+            }
+
             long now = System.nanoTime();
             if (timestampNanos > now) {
                 Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
@@ -691,12 +726,7 @@
                 timestampNanos = now;
             }
 
-            if (mHavePendingVsync) {
-                Log.w(TAG, "Already have a pending vsync event.  There should only be "
-                        + "one at a time.");
-            } else {
-                mHavePendingVsync = true;
-            }
+            mHandler.removeMessages(MSG_FAKE_VSYNC);
 
             mTimestampNanos = timestampNanos;
             mFrame = frame;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 816b631..db3ba40 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1047,36 +1047,50 @@
     /**
      * The accessibility focus which is the current user position when
      * interacting with the accessibility framework.
+     *
+     * @hide
      */
     public static final int FOCUS_ACCESSIBILITY =  0x00001000;
 
     /**
      * Use with {@link #focusSearch(int)}. Move acessibility focus left.
+     *
+     * @hide
      */
     public static final int ACCESSIBILITY_FOCUS_LEFT = FOCUS_LEFT | FOCUS_ACCESSIBILITY;
 
     /**
      * Use with {@link #focusSearch(int)}. Move acessibility focus up.
+     *
+     * @hide
      */
     public static final int ACCESSIBILITY_FOCUS_UP = FOCUS_UP | FOCUS_ACCESSIBILITY;
 
     /**
      * Use with {@link #focusSearch(int)}. Move acessibility focus right.
+     *
+     * @hide
      */
     public static final int ACCESSIBILITY_FOCUS_RIGHT = FOCUS_RIGHT | FOCUS_ACCESSIBILITY;
 
     /**
      * Use with {@link #focusSearch(int)}. Move acessibility focus down.
+     *
+     * @hide
      */
     public static final int ACCESSIBILITY_FOCUS_DOWN = FOCUS_DOWN | FOCUS_ACCESSIBILITY;
 
     /**
      * Use with {@link #focusSearch(int)}. Move acessibility focus forward.
+     *
+     * @hide
      */
     public static final int ACCESSIBILITY_FOCUS_FORWARD = FOCUS_FORWARD | FOCUS_ACCESSIBILITY;
 
     /**
      * Use with {@link #focusSearch(int)}. Move acessibility focus backward.
+     *
+     * @hide
      */
     public static final int ACCESSIBILITY_FOCUS_BACKWARD = FOCUS_BACKWARD | FOCUS_ACCESSIBILITY;
 
@@ -6333,6 +6347,31 @@
         }
     }
 
+    private void sendAccessibilityHoverEvent(int eventType) {
+        // Since we are not delivering to a client accessibility events from not
+        // important views (unless the clinet request that) we need to fire the
+        // event from the deepest view exposed to the client. As a consequence if
+        // the user crosses a not exposed view the client will see enter and exit
+        // of the exposed predecessor followed by and enter and exit of that same
+        // predecessor when entering and exiting the not exposed descendant. This
+        // is fine since the client has a clear idea which view is hovered at the
+        // price of a couple more events being sent. This is a simple and
+        // working solution.
+        View source = this;
+        while (true) {
+            if (source.includeForAccessibility()) {
+                source.sendAccessibilityEvent(eventType);
+                return;
+            }
+            ViewParent parent = source.getParent();
+            if (parent instanceof View) {
+                source = (View) parent;
+            } else {
+                return;
+            }
+        }
+    }
+
     private void requestAccessibilityFocusFromHover() {
         if (includeForAccessibility() && isActionableForAccessibility()) {
             requestAccessibilityFocus();
@@ -7902,16 +7941,15 @@
                     || action == MotionEvent.ACTION_HOVER_MOVE)
                     && !hasHoveredChild()
                     && pointInView(event.getX(), event.getY())) {
-                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+                sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
                 mSendingHoverAccessibilityEvents = true;
-                requestAccessibilityFocusFromHover();
             }
         } else {
             if (action == MotionEvent.ACTION_HOVER_EXIT
                     || (action == MotionEvent.ACTION_MOVE
                             && !pointInView(event.getX(), event.getY()))) {
                 mSendingHoverAccessibilityEvents = false;
-                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+                sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
                 // If the window does not have input focus we take away accessibility
                 // focus as soon as the user stop hovering over the view.
                 if (mAttachInfo != null && !mAttachInfo.mHasWindowFocus) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cdc51d1..51fd346 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3926,10 +3926,9 @@
     }
     
     public void dumpGfxInfo(int[] info) {
+        info[0] = info[1] = 0;
         if (mView != null) {
             getGfxInfo(mView, info);
-        } else {
-            info[0] = info[1] = 0;
         }
     }
 
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 5d33cec..dd6b537 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -530,13 +530,16 @@
 
                     for (int i = 0; i < count; i++) {
                         ViewRootImpl root = mRoots[i];
+                        String name = getWindowName(root);
+                        pw.printf("\n\t%s", name);
+
                         HardwareRenderer renderer = root.getView().mAttachInfo.mHardwareRenderer;
                         if (renderer != null) {
                             renderer.dumpGfxInfo(pw);
                         }
                     }
 
-                    pw.println("\nView hierarchy:");
+                    pw.println("\nView hierarchy:\n");
 
                     int viewsCount = 0;
                     int displayListsSize = 0;
@@ -546,15 +549,14 @@
                         ViewRootImpl root = mRoots[i];
                         root.dumpGfxInfo(info);
 
-                        String name = root.getClass().getName() + '@' +
-                                Integer.toHexString(hashCode());                        
-                        pw.printf("  %s: %d views, %.2f kB (display lists)",
+                        String name = getWindowName(root);
+                        pw.printf("  %s\n  %d views, %.2f kB of display lists",
                                 name, info[0], info[1] / 1024.0f);
                         HardwareRenderer renderer = root.getView().mAttachInfo.mHardwareRenderer;
                         if (renderer != null) {
                             pw.printf(", %d frames rendered", renderer.getFrameCount());
                         }
-                        pw.printf("\n");
+                        pw.printf("\n\n");
 
                         viewsCount += info[0];
                         displayListsSize += info[1];
@@ -570,6 +572,11 @@
         }        
     }
 
+    private static String getWindowName(ViewRootImpl root) {
+        return root.mWindowAttributes.getTitle() + "/" +
+                root.getClass().getName() + '@' + Integer.toHexString(root.hashCode());
+    }
+
     public void setStoppedState(IBinder token, boolean stopped) {
         synchronized (this) {
             if (mViews == null)
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 3834fd6..3ad3a55 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -469,13 +469,7 @@
      *     {@link View#FOCUS_LEFT},
      *     {@link View#FOCUS_RIGHT},
      *     {@link View#FOCUS_FORWARD},
-     *     {@link View#FOCUS_BACKWARD},
-     *     {@link View#ACCESSIBILITY_FOCUS_FORWARD},
-     *     {@link View#ACCESSIBILITY_FOCUS_BACKWARD},
-     *     {@link View#ACCESSIBILITY_FOCUS_UP},
-     *     {@link View#ACCESSIBILITY_FOCUS_RIGHT},
-     *     {@link View#ACCESSIBILITY_FOCUS_DOWN},
-     *     {@link View#ACCESSIBILITY_FOCUS_LEFT}.
+     *     {@link View#FOCUS_BACKWARD}.
      *
      * @return The node info for the view that can take accessibility focus.
      */
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index e60716d..c17cb8a 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -180,6 +180,8 @@
      *
      * @see #createAccessibilityNodeInfo(int)
      * @see AccessibilityNodeInfo
+     *
+     * @hide
      */
     public AccessibilityNodeInfo accessibilityFocusSearch(int direction, int virtualViewId) {
         return null;
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index cc490bd..7dfb5bb 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -43,11 +43,6 @@
  * APIs.
  */
 class AccessibilityInjector {
-    // Default result returned from AndroidVox. Using true here means if the
-    // script fails, an accessibility service will always think that traversal
-    // has succeeded.
-    private static final String DEFAULT_ANDROIDVOX_RESULT = "true";
-
     // The WebViewClassic this injector is responsible for managing.
     private final WebViewClassic mWebViewClassic;
 
@@ -488,15 +483,19 @@
             switch (action) {
                 case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
                 case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
-                    final int granularity = arguments.getInt(
-                            AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
-                    mAccessibilityJSONObject.accumulate("granularity", granularity);
+                    if (arguments != null) {
+                        final int granularity = arguments.getInt(
+                                AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
+                        mAccessibilityJSONObject.accumulate("granularity", granularity);
+                    }
                     break;
                 case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
                 case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
-                    final String element = arguments.getString(
-                            AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING);
-                    mAccessibilityJSONObject.accumulate("element", element);
+                    if (arguments != null) {
+                        final String element = arguments.getString(
+                                AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING);
+                        mAccessibilityJSONObject.accumulate("element", element);
+                    }
                     break;
             }
         } catch (JSONException e) {
@@ -505,9 +504,7 @@
 
         final String jsonString = mAccessibilityJSONObject.toString();
         final String jsCode = String.format(ACCESSIBILITY_ANDROIDVOX_TEMPLATE, jsonString);
-        final String result = mCallback.performAction(mWebView, jsCode, DEFAULT_ANDROIDVOX_RESULT);
-
-        return ("true".equalsIgnoreCase(result));
+        return mCallback.performAction(mWebView, jsCode);
     }
 
     /**
@@ -518,13 +515,13 @@
                 "javascript:(function() { %s.onResult(%d, %s); })();";
 
         // Time in milliseconds to wait for a result before failing.
-        private static final long RESULT_TIMEOUT = 200;
+        private static final long RESULT_TIMEOUT = 5000;
 
         private final AtomicInteger mResultIdCounter = new AtomicInteger();
         private final Object mResultLock = new Object();
         private final String mInterfaceName;
 
-        private String mResult = null;
+        private boolean mResult = false;
         private long mResultId = -1;
 
         private CallbackHandler(String interfaceName) {
@@ -536,29 +533,27 @@
          *
          * @param webView The WebView to perform the action on.
          * @param code JavaScript code that evaluates to a result.
-         * @param defaultResult The result to return if the action times out.
          * @return The result of the action, or false if it timed out.
          */
-        private String performAction(WebView webView, String code, String defaultResult) {
+        private boolean performAction(WebView webView, String code) {
             final int resultId = mResultIdCounter.getAndIncrement();
             final String url = String.format(
                     JAVASCRIPT_ACTION_TEMPLATE, mInterfaceName, resultId, code);
             webView.loadUrl(url);
 
-            return getResultAndClear(resultId, defaultResult);
+            return getResultAndClear(resultId);
         }
 
         /**
          * Gets the result of a request to perform an accessibility action.
          *
          * @param resultId The result id to match the result with the request.
-         * @param defaultResult The default result to return on timeout.
          * @return The result of the request.
          */
-        private String getResultAndClear(int resultId, String defaultResult) {
+        private boolean getResultAndClear(int resultId) {
             synchronized (mResultLock) {
                 final boolean success = waitForResultTimedLocked(resultId);
-                final String result = success ? mResult : defaultResult;
+                final boolean result = success ? mResult : false;
                 clearResultLocked();
                 return result;
             }
@@ -569,7 +564,7 @@
          */
         private void clearResultLocked() {
             mResultId = -1;
-            mResult = null;
+            mResult = false;
         }
 
         /**
@@ -620,7 +615,7 @@
 
             synchronized (mResultLock) {
                 if (resultId > mResultId) {
-                    mResult = result;
+                    mResult = Boolean.parseBoolean(result);
                     mResultId = resultId;
                 }
                 mResultLock.notifyAll();
diff --git a/core/java/android/webkit/AccessibilityInjectorFallback.java b/core/java/android/webkit/AccessibilityInjectorFallback.java
index 4d9c26c..783b3db 100644
--- a/core/java/android/webkit/AccessibilityInjectorFallback.java
+++ b/core/java/android/webkit/AccessibilityInjectorFallback.java
@@ -272,11 +272,19 @@
     boolean performAccessibilityAction(int action, Bundle arguments) {
         switch (action) {
             case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
-            case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
+            case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: {
                 final int direction = getDirectionForAction(action);
                 final int axis = getAxisForGranularity(arguments.getInt(
                         AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT));
                 return traverseGivenAxis(direction, axis, true, null);
+            }
+            case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
+            case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: {
+                final int direction = getDirectionForAction(action);
+                // TODO: Add support for moving by object.
+                final int axis = NAVIGATION_AXIS_SENTENCE;
+                return traverseGivenAxis(direction, axis, true, null);
+            }
             default:
                 return false;
         }
@@ -291,8 +299,10 @@
      */
     private static int getDirectionForAction(int action) {
         switch (action) {
+            case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
             case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
                 return NAVIGATION_DIRECTION_FORWARD;
+            case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
             case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
                 return NAVIGATION_DIRECTION_BACKWARD;
             default:
@@ -316,8 +326,8 @@
             case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE:
                 return NAVIGATION_AXIS_SENTENCE;
             case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH:
-                // TODO: Figure out what nextSibling() actually means.
-                return NAVIGATION_AXIS_SIBLING;
+                // TODO: This should map to object once we implement it.
+                return NAVIGATION_AXIS_SENTENCE;
             case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE:
                 return NAVIGATION_AXIS_DOCUMENT;
             default:
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 62bc502..33eaad6 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -194,13 +194,6 @@
             mCanPause = mCanSeekBack = mCanSeekForward = true;
         }
 
-        // mMediaController status depends on the Metadata result, so put it
-        // after reading the MetaData
-        if (mMediaController != null) {
-            mMediaController.setEnabled(true);
-            mMediaController.show();
-        }
-
         if (mProgressView != null) {
             mProgressView.setVisibility(View.GONE);
         }
@@ -215,6 +208,16 @@
 
         if (getStartWhenPrepared()) {
             mPlayer.start();
+            // Clear the flag.
+            setStartWhenPrepared(false);
+        }
+
+        // mMediaController status depends on the Metadata result, so put it
+        // after reading the MetaData.
+        // And make sure mPlayer state is updated before showing the controller.
+        if (mMediaController != null) {
+            mMediaController.setEnabled(true);
+            mMediaController.show();
         }
     }
 
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index 90db308..ab884df 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -147,6 +147,7 @@
                 // Save the inline video info and inherit it in the full screen
                 int savePosition = 0;
                 boolean canSkipPrepare = false;
+                boolean forceStart = false;
                 if (mHTML5VideoView != null) {
                     // We don't allow enter full screen mode while the previous
                     // full screen video hasn't finished yet.
@@ -154,11 +155,11 @@
                         Log.w(LOGTAG, "Try to reenter the full screen mode");
                         return;
                     }
+                    int playerState = mHTML5VideoView.getCurrentState();
                     // If we are playing the same video, then it is better to
                     // save the current position.
                     if (layerId == mHTML5VideoView.getVideoLayerId()) {
                         savePosition = mHTML5VideoView.getCurrentPosition();
-                        int playerState = mHTML5VideoView.getCurrentState();
                         canSkipPrepare = (playerState == HTML5VideoView.STATE_PREPARING
                                 || playerState == HTML5VideoView.STATE_PREPARED
                                 || playerState == HTML5VideoView.STATE_PLAYING)
@@ -166,10 +167,14 @@
                     }
                     if (!canSkipPrepare) {
                         mHTML5VideoView.reset();
+                    } else {
+                        forceStart = playerState == HTML5VideoView.STATE_PREPARING
+                                || playerState == HTML5VideoView.STATE_PLAYING;
                     }
                 }
                 mHTML5VideoView = new HTML5VideoFullScreen(proxy.getContext(),
                         layerId, savePosition, canSkipPrepare);
+                mHTML5VideoView.setStartWhenPrepared(forceStart);
                 mCurrentProxy = proxy;
                 mHTML5VideoView.setVideoURI(url, mCurrentProxy);
                 mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView);
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
index 354bb5a..1288613 100644
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -22,7 +22,8 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
-import android.util.DisplayMetrics;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.util.EventLog;
 
 import java.util.Locale;
@@ -122,6 +123,7 @@
     private boolean         mLoadWithOverviewMode = false;
     private boolean         mEnableSmoothTransition = false;
     private boolean         mForceUserScalable = false;
+    private boolean         mPasswordEchoEnabled = true;
 
     // AutoFill Profile data
     public static class AutoFillProfile {
@@ -295,6 +297,13 @@
             mAllowUniversalAccessFromFileURLs = true;
             mAllowFileAccessFromFileURLs = true;
         }
+        try {
+            mPasswordEchoEnabled =
+                    Settings.System.getInt(context.getContentResolver(),
+                        Settings.System.TEXT_SHOW_PASSWORD) != 0;
+        } catch (SettingNotFoundException e) {
+            mPasswordEchoEnabled = true;
+        }
     }
 
     private static final String ACCEPT_LANG_FOR_US_LOCALE = "en-US";
diff --git a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
index bfcfdfa..e1a48be 100644
--- a/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
+++ b/core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
@@ -36,6 +36,8 @@
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.Checkable;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.ListView;
@@ -44,6 +46,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.List;
 
 /**
  * This class implements the route chooser dialog for {@link MediaRouter}.
@@ -58,11 +61,7 @@
     private static final int[] ITEM_LAYOUTS = new int[] {
         R.layout.media_route_list_item_top_header,
         R.layout.media_route_list_item_section_header,
-        R.layout.media_route_list_item
-    };
-
-    private static final int[] GROUP_ITEM_LAYOUTS = new int[] {
-        R.layout.media_route_list_item_top_header,
+        R.layout.media_route_list_item,
         R.layout.media_route_list_item_checkable,
         R.layout.media_route_list_item_collapse_group
     };
@@ -74,10 +73,9 @@
     private LauncherListener mLauncherListener;
     private View.OnClickListener mExtendedSettingsListener;
     private RouteAdapter mAdapter;
-    private GroupAdapter mGroupAdapter;
     private ListView mListView;
 
-    static final RouteComparator sComparator = new RouteComparator();
+    final RouteComparator mComparator = new RouteComparator();
 
     public MediaRouteChooserDialogFragment() {
         setStyle(STYLE_NO_TITLE, R.style.Theme_DeviceDefault_Dialog);
@@ -99,12 +97,8 @@
         if (mLauncherListener != null) {
             mLauncherListener.onDetached(this);
         }
-        if (mGroupAdapter != null) {
-            mRouter.removeCallback(mGroupAdapter);
-            mGroupAdapter = null;
-        }
         if (mAdapter != null) {
-            mRouter.removeCallback(mAdapter);
+            mRouter.removeCallback(mAdapter.mCallback);
             mAdapter = null;
         }
         mInflater = null;
@@ -139,45 +133,18 @@
         }
 
         final ListView list = (ListView) layout.findViewById(R.id.list);
-        list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
         list.setItemsCanFocus(true);
         list.setAdapter(mAdapter = new RouteAdapter());
-        list.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
         list.setOnItemClickListener(mAdapter);
 
         mListView = list;
-        mRouter.addCallback(mRouteTypes, mAdapter);
+        mRouter.addCallback(mRouteTypes, mAdapter.mCallback);
+
+        mAdapter.scrollToSelectedItem();
 
         return layout;
     }
 
-    void onExpandGroup(RouteGroup info) {
-        mGroupAdapter = new GroupAdapter(info);
-        mRouter.addCallback(mRouteTypes, mGroupAdapter);
-        mListView.setAdapter(mGroupAdapter);
-        mListView.setOnItemClickListener(mGroupAdapter);
-        mListView.setItemsCanFocus(false);
-        mListView.clearChoices();
-        mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-        mGroupAdapter.initCheckedItems();
-
-        getDialog().setCanceledOnTouchOutside(false);
-    }
-
-    void onDoneGrouping() {
-        mListView.setAdapter(mAdapter);
-        mListView.setOnItemClickListener(mAdapter);
-        mListView.setItemsCanFocus(true);
-        mListView.clearChoices();
-        mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-        mListView.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
-
-        mRouter.removeCallback(mGroupAdapter);
-        mGroupAdapter = null;
-
-        getDialog().setCanceledOnTouchOutside(true);
-    }
-
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         return new RouteChooserDialog(getActivity(), getTheme());
@@ -186,14 +153,6 @@
     @Override
     public void onResume() {
         super.onResume();
-
-        if (mListView != null) {
-            if (mGroupAdapter != null) {
-                mGroupAdapter.initCheckedItems();
-            } else {
-                mListView.setItemChecked(mAdapter.getSelectedRoutePosition(), true);
-            }
-        }
     }
 
     private static class ViewHolder {
@@ -203,52 +162,139 @@
         public ImageButton expandGroupButton;
         public RouteAdapter.ExpandGroupListener expandGroupListener;
         public int position;
+        public CheckBox check;
     }
 
-    private class RouteAdapter extends BaseAdapter implements MediaRouter.Callback,
-            ListView.OnItemClickListener {
+    private class RouteAdapter extends BaseAdapter implements ListView.OnItemClickListener {
         private static final int VIEW_TOP_HEADER = 0;
         private static final int VIEW_SECTION_HEADER = 1;
         private static final int VIEW_ROUTE = 2;
+        private static final int VIEW_GROUPING_ROUTE = 3;
+        private static final int VIEW_GROUPING_DONE = 4;
 
-        private int mSelectedItemPosition;
+        private int mSelectedItemPosition = -1;
         private final ArrayList<Object> mItems = new ArrayList<Object>();
+        final MediaRouterCallback mCallback = new MediaRouterCallback();
+
+        private RouteCategory mCategoryEditingGroups;
+        private RouteGroup mEditingGroup;
+
+        // Temporary lists for manipulation
+        private final ArrayList<RouteInfo> mCatRouteList = new ArrayList<RouteInfo>();
+        private final ArrayList<RouteInfo> mSortRouteList = new ArrayList<RouteInfo>();
+
+        private boolean mIgnoreUpdates;
 
         RouteAdapter() {
             update();
         }
 
         void update() {
-            // TODO this is kind of naive, but our data sets are going to be
-            // fairly small on average.
+            /*
+             * This is kind of wacky, but our data sets are going to be
+             * fairly small on average. Ideally we should be able to do some of this stuff
+             * in-place instead.
+             *
+             * Basic idea: each entry in mItems represents an item in the list for quick access.
+             * Entries can be a RouteCategory (section header), a RouteInfo with a category of
+             * mCategoryEditingGroups (a flattened RouteInfo pulled out of its group, allowing
+             * the user to change the group),
+             */
+            if (mIgnoreUpdates) return;
+
             mItems.clear();
 
             final RouteInfo selectedRoute = mRouter.getSelectedRoute(mRouteTypes);
+            mSelectedItemPosition = -1;
 
-            final ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
+            List<RouteInfo> routes;
             final int catCount = mRouter.getCategoryCount();
             for (int i = 0; i < catCount; i++) {
                 final RouteCategory cat = mRouter.getCategoryAt(i);
-                cat.getRoutes(routes);
+                routes = cat.getRoutes(mCatRouteList);
 
                 mItems.add(cat);
 
-                final int routeCount = routes.size();
-                for (int j = 0; j < routeCount; j++) {
-                    final RouteInfo info = routes.get(j);
-                    if (info == selectedRoute) {
-                        mSelectedItemPosition = mItems.size();
-                    }
-                    mItems.add(info);
+                if (cat == mCategoryEditingGroups) {
+                    addGroupEditingCategoryRoutes(routes);
+                } else {
+                    addSelectableRoutes(selectedRoute, routes);
                 }
+
+                routes.clear();
             }
 
             notifyDataSetChanged();
-            if (mListView != null) {
+            if (mListView != null && mSelectedItemPosition >= 0) {
                 mListView.setItemChecked(mSelectedItemPosition, true);
             }
         }
 
+        void scrollToEditingGroup() {
+            if (mCategoryEditingGroups == null || mListView == null) return;
+
+            int pos = 0;
+            int bound = 0;
+            final int itemCount = mItems.size();
+            for (int i = 0; i < itemCount; i++) {
+                final Object item = mItems.get(i);
+                if (item != null && item == mCategoryEditingGroups) {
+                    bound = i;
+                }
+                if (item == null) {
+                    pos = i;
+                    break; // this is always below the category header; we can stop here.
+                }
+            }
+
+            mListView.smoothScrollToPosition(pos, bound);
+        }
+
+        void scrollToSelectedItem() {
+            if (mListView == null || mSelectedItemPosition < 0) return;
+
+            mListView.smoothScrollToPosition(mSelectedItemPosition);
+        }
+
+        void addSelectableRoutes(RouteInfo selectedRoute, List<RouteInfo> from) {
+            final int routeCount = from.size();
+            for (int j = 0; j < routeCount; j++) {
+                final RouteInfo info = from.get(j);
+                if (info == selectedRoute) {
+                    mSelectedItemPosition = mItems.size();
+                }
+                mItems.add(info);
+            }
+        }
+
+        void addGroupEditingCategoryRoutes(List<RouteInfo> from) {
+            // Unpack groups and flatten for presentation
+            // mSortRouteList will always be empty here.
+            final int topCount = from.size();
+            for (int i = 0; i < topCount; i++) {
+                final RouteInfo route = from.get(i);
+                final RouteGroup group = route.getGroup();
+                if (group == route) {
+                    // This is a group, unpack it.
+                    final int groupCount = group.getRouteCount();
+                    for (int j = 0; j < groupCount; j++) {
+                        final RouteInfo innerRoute = group.getRouteAt(j);
+                        mSortRouteList.add(innerRoute);
+                    }
+                } else {
+                    mSortRouteList.add(route);
+                }
+            }
+            // Sort by name. This will keep the route positions relatively stable even though they
+            // will be repeatedly added and removed.
+            Collections.sort(mSortRouteList, mComparator);
+
+            mItems.addAll(mSortRouteList);
+            mSortRouteList.clear();
+
+            mItems.add(null); // Sentinel reserving space for the "done" button.
+        }
+
         @Override
         public int getCount() {
             return mItems.size();
@@ -256,7 +302,7 @@
 
         @Override
         public int getViewTypeCount() {
-            return 3;
+            return 5;
         }
 
         @Override
@@ -264,7 +310,13 @@
             final Object item = getItem(position);
             if (item instanceof RouteCategory) {
                 return position == 0 ? VIEW_TOP_HEADER : VIEW_SECTION_HEADER;
+            } else if (item == null) {
+                return VIEW_GROUPING_DONE;
             } else {
+                final RouteInfo info = (RouteInfo) item;
+                if (info.getCategory() == mCategoryEditingGroups) {
+                    return VIEW_GROUPING_ROUTE;
+                }
                 return VIEW_ROUTE;
             }
         }
@@ -276,7 +328,14 @@
 
         @Override
         public boolean isEnabled(int position) {
-            return getItemViewType(position) == VIEW_ROUTE;
+            switch (getItemViewType(position)) {
+                case VIEW_ROUTE:
+                case VIEW_GROUPING_ROUTE:
+                case VIEW_GROUPING_DONE:
+                    return true;
+                default:
+                    return false;
+            }
         }
 
         @Override
@@ -301,6 +360,7 @@
                 holder.text1 = (TextView) convertView.findViewById(R.id.text1);
                 holder.text2 = (TextView) convertView.findViewById(R.id.text2);
                 holder.icon = (ImageView) convertView.findViewById(R.id.icon);
+                holder.check = (CheckBox) convertView.findViewById(R.id.check);
                 holder.expandGroupButton = (ImageButton) convertView.findViewById(
                         R.id.expand_button);
                 if (holder.expandGroupButton != null) {
@@ -322,18 +382,25 @@
                 holder.position = position;
             }
 
-            if (viewType == VIEW_ROUTE) {
-                bindItemView(position, holder);
-            } else {
-                bindHeaderView(position, holder);
+            switch (viewType) {
+                case VIEW_ROUTE:
+                case VIEW_GROUPING_ROUTE:
+                    bindItemView(position, holder);
+                    break;
+                case VIEW_SECTION_HEADER:
+                case VIEW_TOP_HEADER:
+                    bindHeaderView(position, holder);
+                    break;
             }
 
+            convertView.setActivated(position == mSelectedItemPosition);
+
             return convertView;
         }
 
         void bindItemView(int position, ViewHolder holder) {
             RouteInfo info = (RouteInfo) mItems.get(position);
-            holder.text1.setText(info.getName());
+            holder.text1.setText(info.getName(getActivity()));
             final CharSequence status = info.getStatus();
             if (TextUtils.isEmpty(status)) {
                 holder.text2.setVisibility(View.GONE);
@@ -351,19 +418,29 @@
 
             RouteCategory cat = info.getCategory();
             boolean canGroup = false;
-            if (cat.isGroupable()) {
-                final RouteGroup group = (RouteGroup) info;
-                canGroup = group.getRouteCount() > 1 ||
-                        getItemViewType(position - 1) == VIEW_ROUTE ||
-                        (position < getCount() - 1 && getItemViewType(position + 1) == VIEW_ROUTE);
+            if (cat == mCategoryEditingGroups) {
+                RouteGroup group = info.getGroup();
+                holder.check.setEnabled(group.getRouteCount() > 1);
+                holder.check.setChecked(group == mEditingGroup);
+            } else {
+                if (cat.isGroupable()) {
+                    final RouteGroup group = (RouteGroup) info;
+                    canGroup = group.getRouteCount() > 1 ||
+                            getItemViewType(position - 1) == VIEW_ROUTE ||
+                            (position < getCount() - 1 &&
+                                    getItemViewType(position + 1) == VIEW_ROUTE);
+                }
             }
-            holder.expandGroupButton.setVisibility(canGroup ? View.VISIBLE : View.GONE);
-            holder.expandGroupListener.position = position;
+
+            if (holder.expandGroupButton != null) {
+                holder.expandGroupButton.setVisibility(canGroup ? View.VISIBLE : View.GONE);
+                holder.expandGroupListener.position = position;
+            }
         }
 
         void bindHeaderView(int position, ViewHolder holder) {
             RouteCategory cat = (RouteCategory) mItems.get(position);
-            holder.text1.setText(cat.getName());
+            holder.text1.setText(cat.getName(getActivity()));
         }
 
         public int getSelectedRoutePosition() {
@@ -371,51 +448,63 @@
         }
 
         @Override
-        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
-            update();
-        }
-
-        @Override
-        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
-            update();
-        }
-
-        @Override
-        public void onRouteAdded(MediaRouter router, RouteInfo info) {
-            update();
-        }
-
-        @Override
-        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
-            update();
-        }
-
-        @Override
-        public void onRouteChanged(MediaRouter router, RouteInfo info) {
-            notifyDataSetChanged();
-        }
-
-        @Override
-        public void onRouteGrouped(MediaRouter router, RouteInfo info,
-                RouteGroup group, int index) {
-            update();
-        }
-
-        @Override
-        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
-            update();
-        }
-
-        @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            ListView lv = (ListView) parent;
-            final Object item = getItem(lv.getCheckedItemPosition());
-            if (!(item instanceof RouteInfo)) {
-                // Oops. Stale event running around? Skip it.
+            final int type = getItemViewType(position);
+            if (type == VIEW_SECTION_HEADER || type == VIEW_TOP_HEADER) {
                 return;
+            } else if (type == VIEW_GROUPING_DONE) {
+                finishGrouping();
+                return;
+            } else {
+                final Object item = getItem(position);
+                if (!(item instanceof RouteInfo)) {
+                    // Oops. Stale event running around? Skip it.
+                    return;
+                }
+
+                final RouteInfo route = (RouteInfo) item;
+                if (type == VIEW_ROUTE) {
+                    mRouter.selectRouteInt(mRouteTypes, route);
+                    dismiss();
+                } else if (type == VIEW_GROUPING_ROUTE) {
+                    final Checkable c = (Checkable) view;
+                    final boolean wasChecked = c.isChecked();
+
+                    mIgnoreUpdates = true;
+                    RouteGroup oldGroup = route.getGroup();
+                    if (!wasChecked && oldGroup != mEditingGroup) {
+                        // Assumption: in a groupable category oldGroup will never be null.
+                        if (mRouter.getSelectedRoute(mRouteTypes) == oldGroup) {
+                            // Old group was selected but is now empty. Select the group
+                            // we're manipulating since that's where the last route went.
+                            mRouter.selectRouteInt(mRouteTypes, mEditingGroup);
+                        }
+                        oldGroup.removeRoute(route);
+                        mEditingGroup.addRoute(route);
+                        c.setChecked(true);
+                    } else if (wasChecked && mEditingGroup.getRouteCount() > 1) {
+                        mEditingGroup.removeRoute(route);
+
+                        // In a groupable category this will add
+                        // the route into its own new group.
+                        mRouter.addRouteInt(route);
+                    }
+                    mIgnoreUpdates = false;
+                    update();
+                }
             }
-            mRouter.selectRoute(mRouteTypes, (RouteInfo) item);
-            dismiss();
+        }
+
+        boolean isGrouping() {
+            return mCategoryEditingGroups != null;
+        }
+
+        void finishGrouping() {
+            mCategoryEditingGroups = null;
+            mEditingGroup = null;
+            getDialog().setCanceledOnTouchOutside(true);
+            update();
+            scrollToSelectedItem();
         }
 
         class ExpandGroupListener implements View.OnClickListener {
@@ -425,251 +514,63 @@
             public void onClick(View v) {
                 // Assumption: this is only available for the user to click if we're presenting
                 // a groupable category, where every top-level route in the category is a group.
-                onExpandGroup((RouteGroup) getItem(position));
-            }
-        }
-    }
-
-    private class GroupAdapter extends BaseAdapter implements MediaRouter.Callback,
-            ListView.OnItemClickListener {
-        private static final int VIEW_HEADER = 0;
-        private static final int VIEW_ROUTE = 1;
-        private static final int VIEW_DONE = 2;
-
-        private RouteGroup mPrimary;
-        private RouteCategory mCategory;
-        private final ArrayList<RouteInfo> mTempList = new ArrayList<RouteInfo>();
-        private final ArrayList<RouteInfo> mFlatRoutes = new ArrayList<RouteInfo>();
-        private boolean mIgnoreUpdates;
-
-        public GroupAdapter(RouteGroup primary) {
-            mPrimary = primary;
-            mCategory = primary.getCategory();
-            update();
-        }
-
-        @Override
-        public int getCount() {
-            return mFlatRoutes.size() + 2;
-        }
-
-        @Override
-        public int getViewTypeCount() {
-            return 3;
-        }
-
-        @Override
-        public int getItemViewType(int position) {
-            if (position == 0) {
-                return VIEW_HEADER;
-            } else if (position == getCount() - 1) {
-                return VIEW_DONE;
-            }
-            return VIEW_ROUTE;
-        }
-
-        void update() {
-            if (mIgnoreUpdates) return;
-            mFlatRoutes.clear();
-            mCategory.getRoutes(mTempList);
-
-            // Unpack groups and flatten for presentation
-            final int topCount = mTempList.size();
-            for (int i = 0; i < topCount; i++) {
-                final RouteInfo route = mTempList.get(i);
-                final RouteGroup group = route.getGroup();
-                if (group == route) {
-                    // This is a group, unpack it.
-                    final int groupCount = group.getRouteCount();
-                    for (int j = 0; j < groupCount; j++) {
-                        final RouteInfo innerRoute = group.getRouteAt(j);
-                        mFlatRoutes.add(innerRoute);
-                    }
-                } else {
-                    mFlatRoutes.add(route);
-                }
-            }
-            mTempList.clear();
-
-            // Sort by name. This will keep the route positions relatively stable even though they
-            // will be repeatedly added and removed.
-            Collections.sort(mFlatRoutes, sComparator);
-            notifyDataSetChanged();
-        }
-
-        void initCheckedItems() {
-            if (mIgnoreUpdates) return;
-            mListView.clearChoices();
-            int count = mFlatRoutes.size();
-            for (int i = 0; i < count; i++){
-                final RouteInfo route = mFlatRoutes.get(i);
-                if (route.getGroup() == mPrimary) {
-                    mListView.setItemChecked(i + 1, true);
-                }
-            }
-        }
-
-        @Override
-        public Object getItem(int position) {
-            if (position == 0) {
-                return mCategory;
-            } else if (position == getCount() - 1) {
-                return null; // Done
-            }
-            return mFlatRoutes.get(position - 1);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-
-        @Override
-        public boolean areAllItemsEnabled() {
-            return false;
-        }
-
-        @Override
-        public boolean isEnabled(int position) {
-            return position > 0;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final int viewType = getItemViewType(position);
-
-            ViewHolder holder;
-            if (convertView == null) {
-                convertView = mInflater.inflate(GROUP_ITEM_LAYOUTS[viewType], parent, false);
-                holder = new ViewHolder();
-                holder.position = position;
-                holder.text1 = (TextView) convertView.findViewById(R.id.text1);
-                holder.text2 = (TextView) convertView.findViewById(R.id.text2);
-                holder.icon = (ImageView) convertView.findViewById(R.id.icon);
-                convertView.setTag(holder);
-            } else {
-                holder = (ViewHolder) convertView.getTag();
-                holder.position = position;
-            }
-
-            if (viewType == VIEW_ROUTE) {
-                bindItemView(position, holder);
-            } else if (viewType == VIEW_HEADER) {
-                bindHeaderView(position, holder);
-            }
-
-            return convertView;
-        }
-
-        void bindItemView(int position, ViewHolder holder) {
-            RouteInfo info = (RouteInfo) getItem(position);
-            holder.text1.setText(info.getName());
-            final CharSequence status = info.getStatus();
-            if (TextUtils.isEmpty(status)) {
-                holder.text2.setVisibility(View.GONE);
-            } else {
-                holder.text2.setVisibility(View.VISIBLE);
-                holder.text2.setText(status);
-            }
-        }
-
-        void bindHeaderView(int position, ViewHolder holder) {
-            holder.text1.setText(mCategory.getName());
-        }
-
-        @Override
-        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
-        }
-
-        @Override
-        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
-        }
-
-        @Override
-        public void onRouteAdded(MediaRouter router, RouteInfo info) {
-            update();
-            initCheckedItems();
-        }
-
-        @Override
-        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
-            if (info == mPrimary) {
-                // Can't keep grouping, clean it up.
-                onDoneGrouping();
-            } else {
+                final RouteGroup group = (RouteGroup) getItem(position);
+                mEditingGroup = group;
+                mCategoryEditingGroups = group.getCategory();
+                getDialog().setCanceledOnTouchOutside(false);
+                mRouter.selectRouteInt(mRouteTypes, mEditingGroup);
                 update();
-                initCheckedItems();
+                scrollToEditingGroup();
             }
         }
 
-        @Override
-        public void onRouteChanged(MediaRouter router, RouteInfo info) {
-            update();
-        }
-
-        @Override
-        public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, int index) {
-            update();
-            initCheckedItems();
-        }
-
-        @Override
-        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
-            update();
-            initCheckedItems();
-        }
-
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            if (getItemViewType(position) == VIEW_DONE) {
-                onDoneGrouping();
-                return;
+        class MediaRouterCallback extends MediaRouter.Callback {
+            @Override
+            public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
+                update();
             }
 
-            final ListView lv = (ListView) parent;
-            final RouteInfo route = mFlatRoutes.get(position - 1);
-            final boolean checked = lv.isItemChecked(position);
+            @Override
+            public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
+                update();
+            }
 
-            mIgnoreUpdates = true;
-            RouteGroup oldGroup = route.getGroup();
-            if (checked && oldGroup != mPrimary) {
-                // Assumption: in a groupable category oldGroup will never be null.
-                oldGroup.removeRoute(route);
+            @Override
+            public void onRouteAdded(MediaRouter router, RouteInfo info) {
+                update();
+            }
 
-                // If the group is now empty, remove the group too.
-                if (oldGroup.getRouteCount() == 0) {
-                    if (mRouter.getSelectedRoute(mRouteTypes) == oldGroup) {
-                        // Old group was selected but is now empty. Select the group
-                        // we're manipulating since that's where the last route went.
-                        mRouter.selectRoute(mRouteTypes, mPrimary);
-                    }
-                    mRouter.removeRouteInt(oldGroup);
+            @Override
+            public void onRouteRemoved(MediaRouter router, RouteInfo info) {
+                if (info == mEditingGroup) {
+                    finishGrouping();
                 }
-
-                mPrimary.addRoute(route);
-            } else if (!checked) {
-                if (mPrimary.getRouteCount() > 1) {
-                    mPrimary.removeRoute(route);
-
-                    // In a groupable category this will add the route into its own new group.
-                    mRouter.addRouteInt(route);
-                } else {
-                    // We're about to remove the last route.
-                    // Don't let this happen, as it would be silly.
-                    // Turn the checkmark back on again. Silly user!
-                    lv.setItemChecked(position, true);
-                }
+                update();
             }
-            mIgnoreUpdates = false;
-            update();
-            initCheckedItems();
+
+            @Override
+            public void onRouteChanged(MediaRouter router, RouteInfo info) {
+                notifyDataSetChanged();
+            }
+
+            @Override
+            public void onRouteGrouped(MediaRouter router, RouteInfo info,
+                    RouteGroup group, int index) {
+                update();
+            }
+
+            @Override
+            public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
+                update();
+            }
         }
     }
 
-    static class RouteComparator implements Comparator<RouteInfo> {
+    class RouteComparator implements Comparator<RouteInfo> {
         @Override
         public int compare(RouteInfo lhs, RouteInfo rhs) {
-            return lhs.getName().toString().compareTo(rhs.getName().toString());
+            return lhs.getName(getActivity()).toString()
+                    .compareTo(rhs.getName(getActivity()).toString());
         }
     }
 
@@ -680,8 +581,8 @@
 
         @Override
         public void onBackPressed() {
-            if (mGroupAdapter != null) {
-                onDoneGrouping();
+            if (mAdapter != null && mAdapter.isGrouping()) {
+                mAdapter.finishGrouping();
             } else {
                 super.onBackPressed();
             }
diff --git a/core/jni/android_bluetooth_HeadsetBase.cpp b/core/jni/android_bluetooth_HeadsetBase.cpp
index 0df211a..34447ef 100644
--- a/core/jni/android_bluetooth_HeadsetBase.cpp
+++ b/core/jni/android_bluetooth_HeadsetBase.cpp
@@ -115,7 +115,7 @@
     pfd.fd = fd;
     pfd.events = POLLIN;
     *err = errno = 0;
-    int ret = poll(&pfd, 1, timeout_ms);
+    int ret = TEMP_FAILURE_RETRY(poll(&pfd, 1, timeout_ms));
     if (ret < 0) {
         ALOGE("poll() error\n");
         *err = errno;
@@ -136,7 +136,7 @@
     while ((int)(bufit - buf) < (len - 1))
     {
         errno = 0;
-        int rc = read(fd, bufit, 1);
+        int rc = TEMP_FAILURE_RETRY(read(fd, bufit, 1));
 
         if (!rc)
             break;
@@ -427,7 +427,7 @@
             {
                 char ch;
                 errno = 0;
-                int nr = read(nat->rfcomm_sock, &ch, 1);
+                int nr = TEMP_FAILURE_RETRY(read(nat->rfcomm_sock, &ch, 1));
                 /* It should be that nr != 1 because we just opened a socket
                    and we haven't sent anything over it for the other side to
                    respond... but one can't be paranoid enough.
diff --git a/core/res/res/layout/media_route_list_item_checkable.xml b/core/res/res/layout/media_route_list_item_checkable.xml
index f6ba09e..d0bffb6 100644
--- a/core/res/res/layout/media_route_list_item_checkable.xml
+++ b/core/res/res/layout/media_route_list_item_checkable.xml
@@ -17,6 +17,7 @@
 <com.android.internal.view.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="match_parent"
               android:layout_height="?android:attr/listPreferredItemHeight"
+              android:background="?android:attr/selectableItemBackground"
               android:gravity="center_vertical">
 
     <ImageView android:layout_width="56dp"
diff --git a/core/res/res/layout/media_route_list_item_collapse_group.xml b/core/res/res/layout/media_route_list_item_collapse_group.xml
index 3f4b1c0..d605c18 100644
--- a/core/res/res/layout/media_route_list_item_collapse_group.xml
+++ b/core/res/res/layout/media_route_list_item_collapse_group.xml
@@ -14,26 +14,31 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
-              android:layout_height="?android:attr/listPreferredItemHeightSmall"
-              android:background="#19ffffff"
-              android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
-              android:paddingRight="?android:attr/listPreferredItemPaddingRight"
-              android:gravity="center_vertical">
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:background="?android:attr/selectableItemBackground">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="?android:attr/listPreferredItemHeightSmall"
+        android:background="#19ffffff"
+        android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+        android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+        android:gravity="center_vertical">
 
-    <TextView android:layout_width="0dp"
-              android:layout_height="wrap_content"
-              android:layout_weight="1"
-              android:singleLine="true"
-              android:ellipsize="marquee"
-              android:text="@string/media_route_chooser_grouping_done"
-              android:textAppearance="?android:attr/textAppearanceMedium" />
+        <TextView android:layout_width="0dp"
+                  android:layout_height="wrap_content"
+                  android:layout_weight="1"
+                  android:singleLine="true"
+                  android:ellipsize="marquee"
+                  android:text="@string/media_route_chooser_grouping_done"
+                  android:textAppearance="?android:attr/textAppearanceMedium" />
 
-    <ImageView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:src="@drawable/ic_media_group_collapse"
-        android:scaleType="center" />
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_media_group_collapse"
+            android:scaleType="center" />
 
-</LinearLayout>
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index a19bc2b..3d63e84 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1290,6 +1290,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dokluidsprekers"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI-klank"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Stelsel"</string>
+    <string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth-oudio"</string>
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index d2cb8bc..c288b2b 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"የትከል ድምፅ ማጉያዎች"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI ድምጽ"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"ስርዓት"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index aea32a8..8fe0c21 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"مكبرات صوت للإرساء"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"صوت HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"النظام"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index bfe0e27..6ec4798 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Дынамікі станцыi"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI-аўдыёвыхад"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Сістэма"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index a972258..fa274a3 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Докинг станц.: Високогов."</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI аудио"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 8f8fb1d..3340d0c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -170,7 +170,7 @@
     <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"Informació personal"</string>
     <string name="permgroupdesc_personalInfo" product="tablet" msgid="6975389054186265786">"Accés directe als contactes i al calendari emmagatzemat a la tauleta."</string>
     <string name="permgroupdesc_personalInfo" product="default" msgid="5488050357388806068">"Accés directe als contactes i al calendari emmagatzemats al telèfon."</string>
-    <string name="permgrouplab_location" msgid="635149742436692049">"Ubicació"</string>
+    <string name="permgrouplab_location" msgid="635149742436692049">"La teva ubicació"</string>
     <string name="permgroupdesc_location" msgid="5704679763124170100">"Supervisa la teva ubicació física."</string>
     <string name="permgrouplab_network" msgid="5808983377727109831">"Comunicació de xarxa"</string>
     <string name="permgroupdesc_network" msgid="4478299413241861987">"Accedeix a diverses funcions de xarxa."</string>
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"suprimeix"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Mètode d\'entrada"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Accions de text"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"S\'està acabant l\'espai d\'emmagatzematge"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"És possible que algunes funcions del sistema no funcionin"</string>
     <string name="ok" msgid="5970060430562524910">"D\'acord"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
     <string name="yes" msgid="5362982303337969312">"D\'acord"</string>
@@ -1290,6 +1288,6 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altaveus del connector"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Àudio HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
-    <skip />
+    <string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Àudio per Bluetooth"</string>
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fet"</string>
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 35f0480..df195a3 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Reproduktory doku"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Zvuk HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Systém"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 6bc5f9f..25cba5f 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"slet"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Inputmetode"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der er snart ikke mere lagerplads"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nogle systemfunktioner virker måske ikke"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Annuller"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dockstationens højttalere"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI-lyd"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Udfør"</string>
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 2bd623b..1aec2fa 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dock-Lautsprecher"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI-Audio"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 9f50ca3..b223f00 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1290,6 +1290,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Ηχεία βάσης σύνδεσης"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Ήχος HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Σύστημα"</string>
+    <string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Ήχος Bluetooth"</string>
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 1386ad5..6596393 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"delete"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Input method"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Text actions"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancel"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1290,6 +1288,6 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dock speakers"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI audio"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
-    <skip />
+    <string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Bluetooth audio"</string>
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Done"</string>
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index c58b08e..674d9c2 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altavoces del conector"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Audio HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 127abb5..b0dd12f 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"eliminar"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de entrada de texto"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no funcionen."</string>
     <string name="ok" msgid="5970060430562524910">"Aceptar"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="yes" msgid="5362982303337969312">"Aceptar"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altavoces del conector"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Audio HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fin"</string>
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 6266353..182f175 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Doki kõlarid"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI heli"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Süsteem"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index fcfbc0f..b633542 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"بلندگوهای جایگاه اتصال"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"صدای HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"سیستم"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 5888acf..84f27dd 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1288,5 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Telineen kaiuttimet"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI-ääni"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Järjestelmä"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Valmis"</string>
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 99c7627..5b13636 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"supprimer"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Mode de saisie"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Actions sur le texte"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Annuler"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Haut-parleurs de la station d\'accueil"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Audio HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Système"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"OK"</string>
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index dbb3878..3ea8ca7 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -371,14 +371,14 @@
     <string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"एप्लिकेशन को मित्रों या सहकर्मियों के ईवेंट के साथ ही वे ईवेंट जोड़ने, निकालने, बदलने देता है जिन्हें आप अपने टेबलेट पर संशोधित कर सकते हैं. इससे एप्लिकेशन, स्‍वामी की जानकारी के बिना उन संदेशों को भेज सकता है जो कैलेंडर स्वामियों की ओर से आते दिखाई देते हैं, या ईवेंट संशोधित कर सकता है."</string>
     <string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"एप्लिकेशन को मित्रों या सहकर्मियों के ईवेंट के साथ ही वे ईवेंट जोड़ने, निकालने, बदलने देता है जिन्हें आप अपने फ़ोन पर संशोधित कर सकते हैं. इससे एप्लिकेशन, स्‍वामी की जानकारी के बिना उन संदेशों को भेज सकता है जो कैलेंडर स्वामियों की ओर से आते दिखाई देते हैं, या ईवेंट संशोधित कर सकता है."</string>
     <string name="permlab_accessMockLocation" msgid="8688334974036823330">"परीक्षण के लिए नकली स्‍थान स्रोत"</string>
-    <string name="permdesc_accessMockLocation" msgid="5808711039482051824">"परीक्षण के लिए कृतिम स्थान स्रोत बनाएं या एक नया स्थान प्रदाता इंस्‍टॉल करें. यह एप्लिकेशन को स्‍थान और/या अन्‍य स्थान स्रोतों जैसे GPS या स्‍थान प्रदाताओं द्वारा लौटाई गई स्थिति को ओवरराइड करने देता है."</string>
+    <string name="permdesc_accessMockLocation" msgid="5808711039482051824">"परीक्षण के लिए कृत्रिम स्थान स्रोत बनाएं या एक नया स्थान प्रदाता इंस्‍टॉल करें. यह एप्लिकेशन को स्‍थान और/या अन्‍य स्थान स्रोतों जैसे GPS या स्‍थान प्रदाताओं द्वारा लौटाई गई स्थिति को ओवरराइड करने देता है."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"अतिरिक्त स्‍थान प्रदाता आदेशों में पहुंचे"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="5945166642335800763">"एप्लिकेशन को अतिरिक्त स्थान प्रदाता आदेशों पर पहुंचने देता है. यह एप्लिकेशन को GPS या अन्य स्थान स्रोतों के संचालन में बाधा पहुंचाने दे सकता है."</string>
     <string name="permlab_installLocationProvider" msgid="6578101199825193873">"किसी स्‍थान प्रदाता को इंस्‍टॉल करने की अनुमति"</string>
     <string name="permdesc_installLocationProvider" msgid="9066146120470591509">"परीक्षण के लिए कृत्रिम स्थान स्रोत बनाएं या एक नए स्थान प्रदाता को इंस्‍टॉल करें. यह एप्लिकेशन को स्‍थान और/या अन्‍य स्थान स्रोतों जैसे GPS या स्‍थान प्रदाताओं द्वारा लौटाई गई स्थिति को ओवरराइड करने देता है."</string>
     <string name="permlab_accessFineLocation" msgid="5885550969882561436">"सटीक (GPS) स्‍थान"</string>
     <string name="permdesc_accessFineLocation" product="tablet" msgid="8960597421469894181">"टेबलेट पर सटीक स्थान स्रोतों जैसे ग्लोबल पोजिशनिंग सिस्टम तक पहुंचें. जब स्थान सेवाएं उपलब्ध और चालू हैं, तो यह यह अनुमति एप्लिकेशन को आपका सटीक स्थान निर्धारित करने देती है."</string>
-    <string name="permdesc_accessFineLocation" product="default" msgid="239268765496141815">"फ़ोन पर सटीक स्थान स्रोतों जैसे ग्लोबल पोजिशनिंग सिस्टम तक पहुंचें. जब स्थान सेवाएं उपलब्ध और चालू हों, तो यह यह अनुमति एप्लिकेशन को आपका सटीक स्थान निर्धारित करने देती है."</string>
+    <string name="permdesc_accessFineLocation" product="default" msgid="239268765496141815">"फ़ोन पर सटीक स्थान स्रोतों जैसे ग्लोबल पोजिशनिंग सिस्टम तक पहुंचें. जब स्थान सेवाएं उपलब्ध और चालू हों, तो यह अनुमति एप्लिकेशन को आपका सटीक स्थान निर्धारित करने देती है."</string>
     <string name="permlab_accessCoarseLocation" msgid="7422827215441638984">"अनुमानित (नेटवर्क-आधारित) स्‍थान"</string>
     <string name="permdesc_accessCoarseLocation" msgid="5383798877137640762">"उपलब्‍ध नेटवर्क स्रोतों जैसे सेल टॉवर और WI- Fi का उपयोग करते हुए स्‍थान प्रदाताओं से अनुमानित स्थान तक पहुचें. जब ये स्थान सेवाएं उपलब्ध और चालू हों, तो यह अनुमति एप्लिकेशन को आपका अनुमानित स्थान निर्धारित करते देती है."</string>
     <string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"SurfaceFlinger में पहुंचें"</string>
@@ -526,7 +526,7 @@
     <string name="permdesc_writeDictionary" msgid="8185385716255065291">"एप्लिकेशन को उपयोगकर्ता डिक्शनरी में नए शब्द लिखने देता है."</string>
     <string name="permlab_sdcardRead" product="nosdcard" msgid="8235341515605559677">"संरक्ष‍ित संग्रहण पर पहुंच का परीक्षण करें"</string>
     <string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"संरक्ष‍ित संग्रहण पर पहुंच का परीक्षण करें"</string>
-    <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"एप्लि. को USB संग्रहण हेतु अनुमति का परीक्षण करने देता है जो भविष्‍य के उपकरणों में उपलब्‍ध होगा."</string>
+    <string name="permdesc_sdcardRead" product="nosdcard" msgid="5791957130190763289">"एप्लि. को USB संग्रहण अनुमति जांचने देता है जो भावी उपकरणों में उपलब्‍ध होगा."</string>
     <string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"एप्लिकेशन को SD कार्ड के लिए किसी अनुमति का परीक्षण करने देता है जो भविष्‍य के उपकरणों में उपलब्‍ध होगा."</string>
     <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"अपने USB संग्रहण की सामग्री संशोधित करें या हटाएं"</string>
     <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"अपने SD कार्ड की सामग्री संशोधित करें या हटाएं"</string>
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"डॉक स्‍पीकर"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI ऑडियो"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"सिस्‍टम"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index dbe54ea..03bdd7d 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Zvučnici postolja"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI audio"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sustav"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 79e3119..8a65df8 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dokkolóegység hangszórója"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI audió"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Rendszer"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 28cc099..7540ba1 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Pengeras suara dok"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Audio HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index bf7a168..dfa1133 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1288,5 +1288,6 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altoparlanti dock"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Audio HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
+    <string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string>
     <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fine"</string>
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 880cc9d..5ad9066 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"רמקולים של מעגן"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"אודיו HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"מערכת"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index eb3d96f..f58fe5d 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -511,9 +511,9 @@
     <string name="permlab_disableKeyguard" msgid="3598496301486439258">"画面ロックの無効化"</string>
     <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"キーロックとキーロックに関連付けられたパスワードのセキュリティを無効にすることをアプリに許可します。たとえば、かかってきた電話を受ける際にキーロックを無効にし、通話が終了したらキーロックを再度有効にする場合などに使用します。"</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"同期設定の読み取り"</string>
-    <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"アカウントの同期設定の読み取りをアプリに許可します。たとえば、Peopleアプリがアカウントと同期しているかどうかをアプリから特定できるようになります。"</string>
+    <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"アカウントの同期設定の読み取りをアプリに許可します。たとえば、連絡帳アプリがアカウントと同期しているかどうかをアプリから特定できるようになります。"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"同期のON/OFFの切り替え"</string>
-    <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"アカウントの同期設定の変更をアプリに許可します。たとえば、Peopleアプリとアカウントの同期を有効にするために使用できます。"</string>
+    <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"アカウントの同期設定の変更をアプリに許可します。たとえば、連絡帳アプリとアカウントの同期を有効にするために使用できます。"</string>
     <string name="permlab_readSyncStats" msgid="7396577451360202448">"同期統計の読み取り"</string>
     <string name="permdesc_readSyncStats" msgid="1510143761757606156">"アカウントの同期ステータス(同期イベントの履歴、同期されたデータの量など)の読み取りをアプリに許可します。"</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"登録したフィードの読み取り"</string>
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"削除"</string>
     <string name="inputMethod" msgid="1653630062304567879">"入力方法"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"テキスト操作"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"空き容量わずか"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"一部のシステム機能が動作しない可能性があります"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ホルダーのスピーカー"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMIオーディオ"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"システム"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"完了"</string>
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index d345c91..2ac3abe 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"도크 스피커"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI 오디오"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"시스템"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 319aca7..7c188cb 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Doko garsiakalbiai"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI garsas"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 8839657..b8499b69 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Doka skaļruņi"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI audio"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistēma"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 41f8521..9725b0f 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Pembesar suara dok"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Audio HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 24d8497..3c53149 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"slett"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Inndatametode"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lite ledig lagringsplass"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dokkhøyttalere"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI-lyd"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Fullført"</string>
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index fb5e365..2efe643 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dockluidsprekers"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI-audio"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Systeem"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 8bd9b91..fc4293d 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"usuń"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Sposób wprowadzania tekstu"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Działania na tekście"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kończy się miejsce"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektóre funkcje systemu mogą nie działać"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Anuluj"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Głośniki stacji dokującej"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Dźwięk przez HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Gotowe"</string>
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 1e2687d..9073976 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"eliminar"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Acções de texto"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Está quase sem espaço de armazenamento"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema poderão não funcionar"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Altif. estação ancoragem"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Áudio HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Concluído"</string>
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 75840cc..440627c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes do dock"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Áudio HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistema"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index d5a4250..3a229f7 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -2024,6 +2024,8 @@
     <skip />
     <!-- no translation found for default_audio_route_category_name (3722811174003886946) -->
     <skip />
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ef732f8..a2afc72 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Difuz. dispozit. andocare"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Ieşire audio HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 8c280cc..a0a7dea 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Динамики док-станции"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI-аудио"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 0ddf9ef..c96f108 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Reproduktory doku"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Zvuk HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Systém"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 102e6f2..feaa71a 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Zvočniki stojala"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Zvok HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7dd17fc..b3e0704 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Звучници базне станице"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI аудио"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Систем"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index c673628..f94fe3f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"ta bort"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Indatametod"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Textåtgärder"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lagringsutrymmet börjar ta slut"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Dockningsstationens högtalare"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI-ljud"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Klar"</string>
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index ef9d19d..11e6a7d 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Vipasa sauti vya gati"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Sauti ya HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Mfumo"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index c4b31d5..008ceb5 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"ลบ"</string>
     <string name="inputMethod" msgid="1653630062304567879">"วิธีป้อนข้อมูล"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"การทำงานของข้อความ"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"พื้นที่จัดเก็บเหลือน้อย"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
     <string name="ok" msgid="5970060430562524910">"ตกลง"</string>
     <string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
     <string name="yes" msgid="5362982303337969312">"ตกลง"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"ลำโพงแท่นชาร์จ"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"เสียง HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"ระบบ"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"เสร็จสิ้น"</string>
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 4ea1dbc..1b597a6 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Mga speaker ng dock"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI audio"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"System"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 182e2cd..6450fcc 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -822,8 +822,8 @@
     <string name="searchview_description_submit" msgid="2688450133297983542">"Sorguyu gönder"</string>
     <string name="searchview_description_voice" msgid="2453203695674994440">"Sesli arama"</string>
     <string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Dokunarak Keşfet etkinleştirilsin mi?"</string>
-    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> Dokunarak Keşfet özelliğini etkinleştirmek istiyor. Dokunarak Keşfet açık olduğunda, parmağınızın altındaki öğelere ait açıklamaları duyabilir veya görebilir ya da tabletle etkileşimde bulunmak için ilgili hareketleri yapabilirsiniz."</string>
-    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g>, Dokunarak Keşfet özelliğini etkinleştirmek istiyor. Dokunarak Keşfet açık olduğunda parmağınızın altındaki öğelere ait açıklamaları duyabilir veya görebilir ya da telefonla etkileşimde bulunmak için ilgili hareketleri yapabilirsiniz."</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> Dokunarak Keşfet özelliğini etkinleştirmek istiyor. Dokunarak Keşfet açık olduğunda, parmağınızın altındaki öğelere ait açıklamaları duyabilir veya görebilir ya da tabletle etkileşimde bulunmak için birtakım hareketler yapabilirsiniz."</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g>, Dokunarak Keşfet özelliğini etkinleştirmek istiyor. Dokunarak Keşfet açık olduğunda parmağınızın altındaki öğelere ait açıklamaları duyabilir veya görebilir ya da telefonla etkileşimde bulunmak için birtakım hareketler yapabilirsiniz."</string>
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"1 ay önce"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"1 ay önce"</string>
   <plurals name="num_seconds_ago">
@@ -1060,7 +1060,7 @@
     <string name="usb_storage_error_message" product="default" msgid="2876018512716970313">"SD kartınızı USB yığın depolama birimi olarak kullanmada sorun var."</string>
     <string name="usb_storage_notification_title" msgid="8175892554757216525">"USB bağlandı"</string>
     <string name="usb_storage_notification_message" msgid="939822783828183763">"Bilgisayarınıza/bilgisayarınızdan dosya kopyalamak için dokunun."</string>
-    <string name="usb_storage_stop_notification_title" msgid="2336058396663516017">"USB depolama birimini kapat"</string>
+    <string name="usb_storage_stop_notification_title" msgid="2336058396663516017">"USB belleğini kapat"</string>
     <string name="usb_storage_stop_notification_message" msgid="1656852098555623822">"USB depolama birimini kapatmak için dokunun."</string>
     <string name="usb_storage_stop_title" msgid="660129851708775853">"USB depolama birimi kullanılıyor"</string>
     <string name="usb_storage_stop_message" product="nosdcard" msgid="4264025280777219521">"USB depolama birimini kapatmadan önce Android\'inizin USB depolama biriminin bilgisayarınızla olan bağlantısını kesin (\"çıkarın\")."</string>
@@ -1094,7 +1094,7 @@
     <string name="candidates_style" msgid="4333913089637062257"><u>"adaylar"</u></string>
     <string name="ext_media_checking_notification_title" product="nosdcard" msgid="3449816005351468560">"USB depolm birimi hazırlanıyor"</string>
     <string name="ext_media_checking_notification_title" product="default" msgid="5457603418970994050">"SD kart hazırlanıyor"</string>
-    <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Hatalar denetleniyor."</string>
+    <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Hata kontrolü yapılıyor"</string>
     <string name="ext_media_nofs_notification_title" product="nosdcard" msgid="7788040745686229307">"Boş USB depolama birimi"</string>
     <string name="ext_media_nofs_notification_title" product="default" msgid="780477838241212997">"Boş SD kart"</string>
     <string name="ext_media_nofs_notification_message" product="nosdcard" msgid="7840121067427269500">"USB depolama birimi boş veya desteklenmeyen bir dosya sistemine sahip."</string>
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Yuva hoparlörleri"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI ses"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Sistem"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index d861415..8939bdb 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"видалити"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Метод введення"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Дії з текстом"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Закінчується пам’ять"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Деякі системні функції можуть не працювати"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Динаміки док-станції"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Аудіовихід HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Система"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Готово"</string>
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e80ac6a..64b4dc7 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -934,10 +934,8 @@
     <string name="deleteText" msgid="7070985395199629156">"xóa"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Phương thức nhập"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Tác vụ văn bản"</string>
-    <!-- no translation found for low_internal_storage_view_title (5576272496365684834) -->
-    <skip />
-    <!-- no translation found for low_internal_storage_view_text (6640505817617414371) -->
-    <skip />
+    <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Sắp hết dung lượng lưu trữ"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Một số chức năng hệ thống có thể không hoạt động"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Hủy"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1290,6 +1288,7 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Loa đế"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Âm thanh HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Hệ thống"</string>
-    <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
     <skip />
+    <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Xong"</string>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 6b1bfa6..22a11d5 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"基座扬声器"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI 音频"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"系统"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 9decfa9..9502622 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"座架喇叭"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"HDMI 音訊"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"系統"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 48a7a8a..f9dae34 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1290,6 +1290,8 @@
     <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Izipika ze-Dock"</string>
     <string name="default_audio_route_name_hdmi" msgid="7986404173839007682">"Umsindo we-HDMI"</string>
     <string name="default_audio_route_category_name" msgid="3722811174003886946">"Isistimu"</string>
+    <!-- no translation found for bluetooth_a2dp_audio_route_name (8575624030406771015) -->
+    <skip />
     <!-- no translation found for media_route_chooser_grouping_done (7966438307723317169) -->
     <skip />
 </resources>
diff --git a/data/sounds/ringtones/ogg/Themos.ogg b/data/sounds/ringtones/ogg/Themos.ogg
index bc850b8..4ddc71c 100644
--- a/data/sounds/ringtones/ogg/Themos.ogg
+++ b/data/sounds/ringtones/ogg/Themos.ogg
Binary files differ
diff --git a/data/sounds/ringtones/wav/Themos.wav b/data/sounds/ringtones/wav/Themos.wav
index d4d5c6e..fa3178f 100644
--- a/data/sounds/ringtones/wav/Themos.wav
+++ b/data/sounds/ringtones/wav/Themos.wav
Binary files differ
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 6b9522b..e88c535 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1271,42 +1271,25 @@
     }
 
     /**
-     * Allow or disallow use of Bluetooth A2DP for media.
-     * <p>The default behavior of the system is to use A2DP for media playback whenever an A2DP sink
-     * is connected. Applications can use this method to override this behavior.
-     * Note that the request will not persist after a wired headset or an A2DP sink is connected or
-     * disconnected:
-     * - Connection of an A2DP sink automatically enables use of A2DP.
-     * - Connection of a wired headset automatically disables use of A2DP.
-     * - Disconnection of a wired headset automatically enables use of A2DP if an A2DP sink is
-     * connected.
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
-     * @param on set <var>true</var> to allow use of A2DP for media (default).
-     *               <var>false</var> to disallow use of A2DP for media.
+     * @param on set <var>true</var> to route A2DP audio to/from Bluetooth
+     *           headset; <var>false</var> disable A2DP audio
      * @deprecated Do not use.
      */
     @Deprecated public void setBluetoothA2dpOn(boolean on){
-        IAudioService service = getService();
-        try {
-            service.setBluetoothA2dpOn(on);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in setBluetoothA2dpOn", e);
-        }
     }
 
     /**
-     * Checks whether use of A2DP sinks is enabled for media.
+     * Checks whether A2DP audio routing to the Bluetooth headset is on or off.
      *
-     * @return true if use of A2DP is enabled for media, false otherwise.
+     * @return true if A2DP audio is being routed to/from Bluetooth headset;
+     *         false if otherwise
      */
     public boolean isBluetoothA2dpOn() {
-        IAudioService service = getService();
-        try {
-            return service.isBluetoothA2dpOn();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in isBluetoothA2dpOn", e);
+        if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP,"")
+            == AudioSystem.DEVICE_STATE_UNAVAILABLE) {
             return false;
+        } else {
+            return true;
         }
     }
 
diff --git a/media/java/android/media/AudioRoutesInfo.aidl b/media/java/android/media/AudioRoutesInfo.aidl
new file mode 100644
index 0000000..d665851
--- /dev/null
+++ b/media/java/android/media/AudioRoutesInfo.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2012, 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 android.media;
+
+parcelable AudioRoutesInfo;
diff --git a/media/java/android/media/AudioRoutesInfo.java b/media/java/android/media/AudioRoutesInfo.java
new file mode 100644
index 0000000..df9fc06
--- /dev/null
+++ b/media/java/android/media/AudioRoutesInfo.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012 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 android.media;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Information available from AudioService about the current routes.
+ * @hide
+ */
+public class AudioRoutesInfo implements Parcelable {
+    static final int MAIN_SPEAKER = 0;
+    static final int MAIN_HEADSET = 1<<0;
+    static final int MAIN_HEADPHONES = 1<<1;
+    static final int MAIN_DOCK_SPEAKERS = 1<<2;
+    static final int MAIN_HDMI = 1<<3;
+
+    CharSequence mBluetoothName;
+    int mMainType = MAIN_SPEAKER;
+
+    public AudioRoutesInfo() {
+    }
+
+    public AudioRoutesInfo(AudioRoutesInfo o) {
+        mBluetoothName = o.mBluetoothName;
+        mMainType = o.mMainType;
+    }
+
+    AudioRoutesInfo(Parcel src) {
+        mBluetoothName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(src);
+        mMainType = src.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        TextUtils.writeToParcel(mBluetoothName, dest, flags);
+        dest.writeInt(mMainType);
+    }
+
+    public static final Parcelable.Creator<AudioRoutesInfo> CREATOR
+            = new Parcelable.Creator<AudioRoutesInfo>() {
+        public AudioRoutesInfo createFromParcel(Parcel in) {
+            return new AudioRoutesInfo(in);
+        }
+
+        public AudioRoutesInfo[] newArray(int size) {
+            return new AudioRoutesInfo[size];
+        }
+    };
+}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index a69912d..ddb7e6b 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -53,6 +53,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -62,6 +63,7 @@
 import android.speech.RecognizerIntent;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.VolumePanel;
@@ -135,10 +137,11 @@
     private static final int MSG_RCDISPLAY_UPDATE = 13;
     private static final int MSG_SET_ALL_VOLUMES = 14;
     private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
+    private static final int MSG_REPORT_NEW_ROUTES = 16;
     // messages handled under wakelock, can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
-    private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 16;
-    private static final int MSG_SET_A2DP_CONNECTION_STATE = 17;
+    private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 17;
+    private static final int MSG_SET_A2DP_CONNECTION_STATE = 18;
 
 
     // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
@@ -397,6 +400,11 @@
     private boolean mBluetoothA2dpEnabled;
     private final Object mBluetoothA2dpEnabledLock = new Object();
 
+    // Monitoring of audio routes.  Protected by mCurAudioRoutes.
+    final AudioRoutesInfo mCurAudioRoutes = new AudioRoutesInfo();
+    final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
+            = new RemoteCallbackList<IAudioRoutesObserver>();
+
     ///////////////////////////////////////////////////////////////////////////
     // Construction
     ///////////////////////////////////////////////////////////////////////////
@@ -3011,6 +3019,26 @@
                     onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
                     mMediaEventWakeLock.release();
                     break;
+
+                case MSG_REPORT_NEW_ROUTES: {
+                    int N = mRoutesObservers.beginBroadcast();
+                    if (N > 0) {
+                        AudioRoutesInfo routes;
+                        synchronized (mCurAudioRoutes) {
+                            routes = new AudioRoutesInfo(mCurAudioRoutes);
+                        }
+                        while (N > 0) {
+                            N--;
+                            IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(N);
+                            try {
+                                obs.dispatchAudioRoutesChanged(routes);
+                            } catch (RemoteException e) {
+                            }
+                        }
+                    }
+                    mRoutesObservers.finishBroadcast();
+                    break;
+                }
             }
         }
     }
@@ -3127,6 +3155,13 @@
                 } else {
                     makeA2dpDeviceUnavailableNow(address);
                 }
+                synchronized (mCurAudioRoutes) {
+                    if (mCurAudioRoutes.mBluetoothName != null) {
+                        mCurAudioRoutes.mBluetoothName = null;
+                        sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
+                                SENDMSG_NOOP, 0, 0, null, 0);
+                    }
+                }
             } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                 if (btDevice.isBluetoothDock()) {
                     // this could be a reconnection after a transient disconnection
@@ -3141,6 +3176,14 @@
                     }
                 }
                 makeA2dpDeviceAvailable(address);
+                synchronized (mCurAudioRoutes) {
+                    String name = btDevice.getAliasName();
+                    if (!TextUtils.equals(mCurAudioRoutes.mBluetoothName, name)) {
+                        mCurAudioRoutes.mBluetoothName = name;
+                        sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
+                                SENDMSG_NOOP, 0, 0, null, 0);
+                    }
+                }
             }
         }
     }
@@ -3204,20 +3247,43 @@
         intent.putExtra("name", name);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
 
+        int connType = 0;
+
         if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
+            connType = AudioRoutesInfo.MAIN_HEADSET;
             intent.setAction(Intent.ACTION_HEADSET_PLUG);
             intent.putExtra("microphone", 1);
         } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
+            connType = AudioRoutesInfo.MAIN_HEADPHONES;
             intent.setAction(Intent.ACTION_HEADSET_PLUG);
             intent.putExtra("microphone", 0);
         } else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
+            connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
             intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
         } else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
+            connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
             intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
         } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
+            connType = AudioRoutesInfo.MAIN_HDMI;
             intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
         }
 
+        synchronized (mCurAudioRoutes) {
+            if (connType != 0) {
+                int newConn = mCurAudioRoutes.mMainType;
+                if (state != 0) {
+                    newConn |= connType;
+                } else {
+                    newConn &= ~connType;
+                }
+                if (newConn != mCurAudioRoutes.mMainType) {
+                    mCurAudioRoutes.mMainType = newConn;
+                    sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
+                            SENDMSG_NOOP, 0, 0, null, 0);
+                }
+            }
+        }
+
         ActivityManagerNative.broadcastStickyIntent(intent, null);
     }
 
@@ -4796,6 +4862,15 @@
     }
 
     @Override
+    public AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
+        synchronized (mCurAudioRoutes) {
+            AudioRoutesInfo routes = new AudioRoutesInfo(mCurAudioRoutes);
+            mRoutesObservers.register(observer);
+            return routes;
+        }
+    }
+
+    @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
 
@@ -4803,5 +4878,8 @@
         dumpFocusStack(pw);
         dumpRCStack(pw);
         dumpStreamStates(pw);
+        pw.println("\nAudio routes:");
+        pw.print("  mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
+        pw.print("  mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
     }
 }
diff --git a/media/java/android/media/IAudioRoutesObserver.aidl b/media/java/android/media/IAudioRoutesObserver.aidl
new file mode 100644
index 0000000..c269b83
--- /dev/null
+++ b/media/java/android/media/IAudioRoutesObserver.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 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 android.media;
+
+import android.media.AudioRoutesInfo;
+
+/**
+ * AIDL for the AudioService to report changes in available audio routes.
+ * @hide
+ */
+oneway interface IAudioRoutesObserver {
+    void dispatchAudioRoutesChanged(in AudioRoutesInfo newRoutes);
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 70fc623..fc5b8f1 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -19,7 +19,9 @@
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothDevice;
 import android.content.ComponentName;
+import android.media.AudioRoutesInfo;
 import android.media.IAudioFocusDispatcher;
+import android.media.IAudioRoutesObserver;
 import android.media.IRemoteControlClient;
 import android.media.IRemoteControlDisplay;
 import android.media.IRingtonePlayer;
@@ -137,4 +139,6 @@
 
     void setWiredDeviceConnectionState(int device, int state, String name);
     int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state);
+
+    AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
 }
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 8488cd2..1086503 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -16,14 +16,14 @@
 
 package android.media;
 
-import android.bluetooth.BluetoothA2dp;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -46,7 +46,7 @@
 
     static class Static {
         final Resources mResources;
-        final AudioManager mAudioManager;
+        final IAudioService mAudioService;
         final Handler mHandler;
         final ArrayList<CallbackInfo> mCallbacks = new ArrayList<CallbackInfo>();
 
@@ -54,39 +54,89 @@
         final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
 
         final RouteCategory mSystemCategory;
-        final HeadphoneChangedBroadcastReceiver mHeadphoneChangedReceiver;
+
+        final AudioRoutesInfo mCurRoutesInfo = new AudioRoutesInfo();
 
         RouteInfo mDefaultAudio;
         RouteInfo mBluetoothA2dpRoute;
 
         RouteInfo mSelectedRoute;
 
+        final IAudioRoutesObserver.Stub mRoutesObserver = new IAudioRoutesObserver.Stub() {
+            public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
+                mHandler.post(new Runnable() {
+                    @Override public void run() {
+                        updateRoutes(newRoutes);
+                    }
+                });
+            }
+        };
+
         Static(Context appContext) {
             mResources = Resources.getSystem();
             mHandler = new Handler(appContext.getMainLooper());
 
-            mAudioManager = (AudioManager)appContext.getSystemService(Context.AUDIO_SERVICE);
+            IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+            mAudioService = IAudioService.Stub.asInterface(b);
 
             // XXX this doesn't deal with locale changes!
             mSystemCategory = new RouteCategory(mResources.getText(
                     com.android.internal.R.string.default_audio_route_category_name),
                     ROUTE_TYPE_LIVE_AUDIO, false);
-
-            final IntentFilter speakerFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
-            speakerFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
-            speakerFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
-            speakerFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
-            mHeadphoneChangedReceiver = new HeadphoneChangedBroadcastReceiver();
-            appContext.registerReceiver(mHeadphoneChangedReceiver, speakerFilter);
         }
 
         // Called after sStatic is initialized
-        void initDefaultRoutes() {
+        void startMonitoringRoutes() {
             mDefaultAudio = new RouteInfo(mSystemCategory);
-            mDefaultAudio.mName = mResources.getText(
-                    com.android.internal.R.string.default_audio_route_name);
+            mDefaultAudio.mNameResId = com.android.internal.R.string.default_audio_route_name;
             mDefaultAudio.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
             addRoute(mDefaultAudio);
+
+            AudioRoutesInfo newRoutes = null;
+            try {
+                newRoutes = mAudioService.startWatchingRoutes(mRoutesObserver);
+            } catch (RemoteException e) {
+            }
+            if (newRoutes != null) {
+                updateRoutes(newRoutes);
+            }
+        }
+
+        void updateRoutes(AudioRoutesInfo newRoutes) {
+            if (newRoutes.mMainType != mCurRoutesInfo.mMainType) {
+                mCurRoutesInfo.mMainType = newRoutes.mMainType;
+                int name;
+                if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0
+                        || (newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADSET) != 0) {
+                    name = com.android.internal.R.string.default_audio_route_name_headphones;
+                } else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
+                    name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
+                } else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
+                    name = com.android.internal.R.string.default_audio_route_name_hdmi;
+                } else {
+                    name = com.android.internal.R.string.default_audio_route_name;
+                }
+                sStatic.mDefaultAudio.mNameResId = name;
+                dispatchRouteChanged(sStatic.mDefaultAudio);
+            }
+            if (!TextUtils.equals(newRoutes.mBluetoothName, mCurRoutesInfo.mBluetoothName)) {
+                mCurRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
+                if (mCurRoutesInfo.mBluetoothName != null) {
+                    if (sStatic.mBluetoothA2dpRoute == null) {
+                        final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
+                        info.mName = mCurRoutesInfo.mBluetoothName;
+                        info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
+                        sStatic.mBluetoothA2dpRoute = info;
+                        addRoute(sStatic.mBluetoothA2dpRoute);
+                    } else {
+                        sStatic.mBluetoothA2dpRoute.mName = mCurRoutesInfo.mBluetoothName;
+                        dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
+                    }
+                } else if (sStatic.mBluetoothA2dpRoute != null) {
+                    removeRoute(sStatic.mBluetoothA2dpRoute);
+                    sStatic.mBluetoothA2dpRoute = null;
+                }
+            }
         }
     }
 
@@ -133,7 +183,7 @@
         synchronized (Static.class) {
             if (sStatic == null) {
                 sStatic = new Static(context.getApplicationContext());
-                sStatic.initDefaultRoutes();
+                sStatic.startMonitoringRoutes();
             }
         }
     }
@@ -155,14 +205,6 @@
         return sStatic.mSelectedRoute;
     }
 
-    static void onHeadphonesPlugged(boolean headphonesPresent, String headphonesName) {
-        sStatic.mDefaultAudio.mName = headphonesPresent
-                ? headphonesName
-                : sStatic.mResources.getText(
-                        com.android.internal.R.string.default_audio_route_name);
-        dispatchRouteChanged(sStatic.mDefaultAudio);
-    }
-
     /**
      * Add a callback to listen to events about specific kinds of media routes.
      * If the specified callback is already registered, its registration will be updated for any
@@ -207,11 +249,25 @@
      * @param route Route to select
      */
     public void selectRoute(int types, RouteInfo route) {
+        // Applications shouldn't programmatically change anything but user routes.
+        types &= ROUTE_TYPE_USER;
+        selectRouteStatic(types, route);
+    }
+    
+    /**
+     * @hide internal use
+     */
+    public void selectRouteInt(int types, RouteInfo route) {
         selectRouteStatic(types, route);
     }
 
     static void selectRouteStatic(int types, RouteInfo route) {
         if (sStatic.mSelectedRoute == route) return;
+        if ((route.getSupportedTypes() & types) == 0) {
+            Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
+                    typesToString(route.getSupportedTypes()) + " into route types " +
+                    typesToString(types));
+        }
 
         if (sStatic.mSelectedRoute != null) {
             // TODO filter types properly
@@ -253,9 +309,9 @@
         if (cat.isGroupable() && !(info instanceof RouteGroup)) {
             // Enforce that any added route in a groupable category must be in a group.
             final RouteGroup group = new RouteGroup(info.getCategory());
-            group.addRoute(info);
             sStatic.mRoutes.add(group);
             dispatchRouteAdded(group);
+            group.addRoute(info);
 
             info = group;
         } else {
@@ -424,6 +480,17 @@
     public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
         return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
     }
+    
+    /**
+     * Create a new route category. Each route must belong to a category.
+     *
+     * @param nameResId Resource ID of the name of the new category
+     * @param isGroupable true if routes in this category may be grouped with one another
+     * @return the new RouteCategory
+     */
+    public RouteCategory createRouteCategory(int nameResId, boolean isGroupable) {
+        return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable);
+    }
 
     static void updateRoute(final RouteInfo info) {
         dispatchRouteChanged(info);
@@ -499,30 +566,20 @@
         }
     }
 
-    static void onA2dpDeviceConnected() {
-        final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
-        info.mName = sStatic.mResources.getString(
-                com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
-        sStatic.mBluetoothA2dpRoute = info;
-        addRoute(sStatic.mBluetoothA2dpRoute);
-    }
-
-    static void onA2dpDeviceDisconnected() {
-        removeRoute(sStatic.mBluetoothA2dpRoute);
-        sStatic.mBluetoothA2dpRoute = null;
-    }
-
     /**
      * Information about a media route.
      */
     public static class RouteInfo {
         CharSequence mName;
+        int mNameResId;
         private CharSequence mStatus;
         int mSupportedTypes;
         RouteGroup mGroup;
         final RouteCategory mCategory;
         Drawable mIcon;
 
+        private Object mTag;
+
         RouteInfo(RouteCategory category) {
             mCategory = category;
         }
@@ -532,6 +589,24 @@
          * to users who may select this as the active route.
          */
         public CharSequence getName() {
+            return getName(sStatic.mResources);
+        }
+        
+        /**
+         * Return the properly localized/resource selected name of this route.
+         * 
+         * @param context Context used to resolve the correct configuration to load
+         * @return The user-friendly name of the media route. This is the string presented
+         * to users who may select this as the active route.
+         */
+        public CharSequence getName(Context context) {
+            return getName(context.getResources());
+        }
+        
+        CharSequence getName(Resources res) {
+            if (mNameResId != 0) {
+                return mName = res.getText(mNameResId);
+            }
             return mName;
         }
 
@@ -574,6 +649,29 @@
             return mIcon;
         }
 
+        /**
+         * Set an application-specific tag object for this route.
+         * The application may use this to store arbitrary data associated with the
+         * route for internal tracking.
+         *
+         * <p>Note that the lifespan of a route may be well past the lifespan of
+         * an Activity or other Context; take care that objects you store here
+         * will not keep more data in memory alive than you intend.</p>
+         *
+         * @param tag Arbitrary, app-specific data for this route to hold for later use
+         */
+        public void setTag(Object tag) {
+            mTag = tag;
+        }
+
+        /**
+         * @return The tag object previously set by the application
+         * @see #setTag(Object)
+         */
+        public Object getTag() {
+            return mTag;
+        }
+
         void setStatusInt(CharSequence status) {
             if (!status.equals(mStatus)) {
                 mStatus = status;
@@ -605,7 +703,6 @@
      */
     public static class UserRouteInfo extends RouteInfo {
         RemoteControlClient mRcc;
-        private Object mTag;
 
         UserRouteInfo(RouteCategory category) {
             super(category);
@@ -620,6 +717,16 @@
             mName = name;
             routeUpdated();
         }
+        
+        /**
+         * Set the user-visible name of this route.
+         * @param resId Resource ID of the name to display to the user to describe this route
+         */
+        public void setName(int resId) {
+            mNameResId = resId;
+            mName = null;
+            routeUpdated();
+        }
 
         /**
          * Set the current user-visible status for this route.
@@ -663,29 +770,6 @@
         public void setIconResource(int resId) {
             setIconDrawable(sStatic.mResources.getDrawable(resId));
         }
-
-        /**
-         * Set an application-specific tag object for this route.
-         * The application may use this to store arbitrary data associated with the
-         * route for internal tracking.
-         *
-         * <p>Note that the lifespan of a route may be well past the lifespan of
-         * an Activity or other Context; take care that objects you store here
-         * will not keep more data in memory alive than you intend.</p>
-         *
-         * @param tag Arbitrary, app-specific data for this route to hold for later use
-         */
-        public void setTag(Object tag) {
-            mTag = tag;
-        }
-
-        /**
-         * @return The tag object previously set by the application
-         * @see #setTag(Object)
-         */
-        public Object getTag() {
-            return mTag;
-        }
     }
 
     /**
@@ -700,9 +784,9 @@
             mGroup = this;
         }
 
-        public CharSequence getName() {
+        CharSequence getName(Resources res) {
             if (mUpdateName) updateName();
-            return super.getName();
+            return super.getName(res);
         }
 
         /**
@@ -831,6 +915,12 @@
         void routeUpdated() {
             int types = 0;
             final int count = mRoutes.size();
+            if (count == 0) {
+                // Don't keep empty groups in the router.
+                MediaRouter.removeRoute(this);
+                return;
+            }
+
             for (int i = 0; i < count; i++) {
                 types |= mRoutes.get(i).mSupportedTypes;
             }
@@ -844,6 +934,7 @@
             final int count = mRoutes.size();
             for (int i = 0; i < count; i++) {
                 final RouteInfo info = mRoutes.get(i);
+                // TODO: There's probably a much more correct way to localize this.
                 if (i > 0) sb.append(", ");
                 sb.append(info.mName);
             }
@@ -870,6 +961,7 @@
      */
     public static class RouteCategory {
         CharSequence mName;
+        int mNameResId;
         int mTypes;
         final boolean mGroupable;
 
@@ -879,10 +971,33 @@
             mGroupable = groupable;
         }
 
+        RouteCategory(int nameResId, int types, boolean groupable) {
+            mNameResId = nameResId;
+            mTypes = types;
+            mGroupable = groupable;
+        }
+
         /**
          * @return the name of this route category
          */
         public CharSequence getName() {
+            return getName(sStatic.mResources);
+        }
+        
+        /**
+         * Return the properly localized/configuration dependent name of this RouteCategory.
+         * 
+         * @param context Context to resolve name resources
+         * @return the name of this route category
+         */
+        public CharSequence getName(Context context) {
+            return getName(context.getResources());
+        }
+        
+        CharSequence getName(Resources res) {
+            if (mNameResId != 0) {
+                return res.getText(mNameResId);
+            }
             return mName;
         }
 
@@ -963,7 +1078,7 @@
      * @see MediaRouter#addCallback(int, Callback)
      * @see MediaRouter#removeCallback(Callback)
      */
-    public interface Callback {
+    public static abstract class Callback {
         /**
          * Called when the supplied route becomes selected as the active route
          * for the given route type.
@@ -972,7 +1087,7 @@
          * @param type Type flag set indicating the routes that have been selected
          * @param info Route that has been selected for the given route types
          */
-        public void onRouteSelected(MediaRouter router, int type, RouteInfo info);
+        public abstract void onRouteSelected(MediaRouter router, int type, RouteInfo info);
 
         /**
          * Called when the supplied route becomes unselected as the active route
@@ -982,7 +1097,7 @@
          * @param type Type flag set indicating the routes that have been unselected
          * @param info Route that has been unselected for the given route types
          */
-        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info);
+        public abstract void onRouteUnselected(MediaRouter router, int type, RouteInfo info);
 
         /**
          * Called when a route for the specified type was added.
@@ -990,7 +1105,7 @@
          * @param router the MediaRouter reporting the event
          * @param info Route that has become available for use
          */
-        public void onRouteAdded(MediaRouter router, RouteInfo info);
+        public abstract void onRouteAdded(MediaRouter router, RouteInfo info);
 
         /**
          * Called when a route for the specified type was removed.
@@ -998,7 +1113,7 @@
          * @param router the MediaRouter reporting the event
          * @param info Route that has been removed from availability
          */
-        public void onRouteRemoved(MediaRouter router, RouteInfo info);
+        public abstract void onRouteRemoved(MediaRouter router, RouteInfo info);
 
         /**
          * Called when an aspect of the indicated route has changed.
@@ -1009,7 +1124,7 @@
          * @param router the MediaRouter reporting the event
          * @param info The route that was changed
          */
-        public void onRouteChanged(MediaRouter router, RouteInfo info);
+        public abstract void onRouteChanged(MediaRouter router, RouteInfo info);
 
         /**
          * Called when a route is added to a group.
@@ -1019,7 +1134,8 @@
          * @param group The group the route was added to
          * @param index The route index within group that info was added at
          */
-        public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, int index);
+        public abstract void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
+                int index);
 
         /**
          * Called when a route is removed from a group.
@@ -1028,15 +1144,15 @@
          * @param info The route that was removed
          * @param group The group the route was removed from
          */
-        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group);
+        public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group);
     }
 
     /**
-     * Stub implementation of the {@link MediaRouter.Callback} interface.
-     * Each interface method is defined as a no-op. Override just the ones
+     * Stub implementation of {@link MediaRouter.Callback}.
+     * Each abstract method is defined as a no-op. Override just the ones
      * you need.
      */
-    public static class SimpleCallback implements Callback {
+    public static class SimpleCallback extends Callback {
 
         @Override
         public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
@@ -1068,44 +1184,4 @@
         }
 
     }
-
-    class BtChangedBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
-                final int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
-                if (state == BluetoothA2dp.STATE_CONNECTED) {
-                    onA2dpDeviceConnected();
-                } else if (state == BluetoothA2dp.STATE_DISCONNECTING ||
-                        state == BluetoothA2dp.STATE_DISCONNECTED) {
-                    onA2dpDeviceDisconnected();
-                }
-            }
-        }
-    }
-
-    static class HeadphoneChangedBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (Intent.ACTION_HEADSET_PLUG.equals(action)) {
-                final boolean plugged = intent.getIntExtra("state", 0) != 0;
-                final String name = sStatic.mResources.getString(
-                        com.android.internal.R.string.default_audio_route_name_headphones);
-                onHeadphonesPlugged(plugged, name);
-            } else if (Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG.equals(action) ||
-                    Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG.equals(action)) {
-                final boolean plugged = intent.getIntExtra("state", 0) != 0;
-                final String name = sStatic.mResources.getString(
-                        com.android.internal.R.string.default_audio_route_name_dock_speakers);
-                onHeadphonesPlugged(plugged, name);
-            } else if (Intent.ACTION_HDMI_AUDIO_PLUG.equals(action)) {
-                final boolean plugged = intent.getIntExtra("state", 0) != 0;
-                final String name = sStatic.mResources.getString(
-                        com.android.internal.R.string.default_audio_route_name_hdmi);
-                onHeadphonesPlugged(plugged, name);
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index aa4e97a..afbceff 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -47,7 +47,7 @@
     <string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"AUTO"</string>
     <string name="status_bar_settings_notifications" msgid="397146176280905137">"Notificaciones"</string>
     <string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth anclado"</string>
-    <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de introducción"</string>
+    <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de entrada"</string>
     <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
     <string name="usb_device_permission_prompt" msgid="834698001271562057">"¿Permitir que la aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda al dispositivo USB?"</string>
     <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"¿Permitir que la aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda al accesorio USB?"</string>
@@ -77,7 +77,7 @@
     <string name="accessibility_home" msgid="8217216074895377641">"Inicio"</string>
     <string name="accessibility_menu" msgid="316839303324695949">"Menú"</string>
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplicaciones recientes"</string>
-    <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botón Cambiar método de introducción"</string>
+    <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botón Cambiar método de entrada"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado"</string>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1eb353f..f18e33e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -117,7 +117,7 @@
     </string>
 
     <!-- Separator for PLMN and SPN in network name. -->
-    <string name="status_bar_network_name_separator" translatable="false">"\n"</string>
+    <string name="status_bar_network_name_separator" translatable="false">|</string>
 
     <!-- Network connection string for Bluetooth Reverse Tethering -->
     <string name="bluetooth_tethered">Bluetooth tethered</string>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index bec9aa2..0bdf84a 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -790,7 +790,7 @@
             if (DEBUG) Log.v(TAG, "Starting activity " + intent);
             context.startActivity(intent, opts.toBundle());
         }
-        if (!usingDrawingCache) {
+        if (usingDrawingCache) {
             holder.thumbnailViewImage.setDrawingCacheEnabled(false);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index baf86f3..3c19ad2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -462,11 +462,12 @@
         signalCluster.setNetworkController(mNetworkController);
 
         if (SHOW_CARRIER_LABEL) {
-            // for wifi-only devices, we show SSID; otherwise, we show PLMN
+            // for mobile devices, we always show mobile connection info here (SPN/PLMN)
+            // for other devices, we show whatever network is connected
             if (mNetworkController.hasMobileDataFeature()) {
                 mNetworkController.addMobileLabelView(mCarrierLabel);
             } else {
-                mNetworkController.addWifiLabelView(mCarrierLabel);
+                mNetworkController.addCombinedLabelView(mCarrierLabel);
             }
         }
 
@@ -820,6 +821,23 @@
             R.integer.config_show_search_delay);
     }
 
+    // Q: What kinds of notifications should show during setup?
+    // A: Almost none! Only things coming from the system (package is "android") that also 
+    // have special "kind" tags marking them as relevant for setup (see below).
+    private boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
+        if ("android".equals(sbn.pkg)) {
+            if (sbn.notification.kind != null) {
+                for (String aKind : sbn.notification.kind) {
+                    // IME switcher, created by InputMethodManagerService
+                    if ("android.system.imeswitcher".equals(aKind)) return true;
+                    // OTA availability & errors, created by SystemUpdateService
+                    if ("android.system.update".equals(aKind)) return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private void loadNotificationShade() {
         if (mPile == null) return;
 
@@ -831,7 +849,7 @@
         // If the device hasn't been through Setup, we only show system notifications
         for (int i=0; i<N; i++) {
             Entry ent = mNotificationData.get(N-i-1);
-            if (provisioned || "android".equals(ent.notification.pkg)) {
+            if (provisioned || showNotificationEvenIfUnprovisioned(ent.notification)) {
                 toShow.add(ent.row);
             }
         }
@@ -886,7 +904,7 @@
         for (int i=0; i<N; i++) {
             Entry ent = mNotificationData.get(N-i-1);
             if ((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
-                    || "android".equals(ent.notification.pkg)) {
+                    || showNotificationEvenIfUnprovisioned(ent.notification)) {
                 toShow.add(ent.icon);
             }
         }
@@ -1376,12 +1394,59 @@
         if (!mTracking)
             return;
         mTracking = false;
-        mPile.setLayerType(View.LAYER_TYPE_NONE, null);
+        setPileLayers(View.LAYER_TYPE_NONE);
         mVelocityTracker.recycle();
         mVelocityTracker = null;
         mCloseView.setPressed(false);
     }
 
+    /**
+     * Enables or disables layers on the children of the notifications pile.
+     * 
+     * When layers are enabled, this method attempts to enable layers for the minimal
+     * number of children. Only children visible when the notification area is fully
+     * expanded will receive a layer. The technique used in this method might cause
+     * more children than necessary to get a layer (at most one extra child with the
+     * current UI.)
+     * 
+     * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE}
+     */
+    private void setPileLayers(int layerType) {
+        final int count = mPile.getChildCount();
+
+        switch (layerType) {
+            case View.LAYER_TYPE_NONE:
+                for (int i = 0; i < count; i++) {
+                    mPile.getChildAt(i).setLayerType(layerType, null);
+                }
+                break;
+            case View.LAYER_TYPE_HARDWARE:
+                final int[] location = new int[2]; 
+                mNotificationPanel.getLocationInWindow(location);
+
+                final int left = location[0];
+                final int top = location[1];
+                final int right = left + mNotificationPanel.getWidth();
+                final int bottom = top + getExpandedViewMaxHeight();
+
+                final Rect childBounds = new Rect();
+
+                for (int i = 0; i < count; i++) {
+                    final View view = mPile.getChildAt(i);
+                    view.getLocationInWindow(location);
+
+                    childBounds.set(location[0], location[1],
+                            location[0] + view.getWidth(), location[1] + view.getHeight());
+
+                    if (childBounds.intersects(left, top, right, bottom)) {
+                        view.setLayerType(layerType, null);
+                    }
+                }
+
+                break;
+        }
+    }
+
     void incrementAnim(long frameTimeNanos) {
         final long deltaNanos = Math.max(frameTimeNanos - mAnimLastTimeNanos, 0);
         final float t = deltaNanos * 0.000000001f;                  // ns -> s
@@ -1421,7 +1486,7 @@
         mCloseView.setPressed(true);
 
         mTracking = true;
-        mPile.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        setPileLayers(View.LAYER_TYPE_HARDWARE);
         mVelocityTracker = VelocityTracker.obtain();
         if (opening) {
             makeExpandedVisible(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index b8f6054..1068267 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -121,8 +121,12 @@
     private int mWimaxSignal = 0;
     private int mWimaxState = 0;
     private int mWimaxExtraState = 0;
+
     // data connectivity (regardless of state, can we access the internet?)
     // state of inet connection - 0 not connected, 100 connected
+    private boolean mConnected = false;
+    private int mConnectedNetworkType = ConnectivityManager.TYPE_NONE;
+    private String mConnectedNetworkTypeName;
     private int mInetCondition = 0;
     private static final int INET_CONDITION_THRESHOLD = 50;
 
@@ -868,6 +872,16 @@
                 .getSystemService(Context.CONNECTIVITY_SERVICE);
         final NetworkInfo info = connManager.getActiveNetworkInfo();
 
+        // Are we connected at all, by any interface?
+        mConnected = info != null && info.isConnected();
+        if (mConnected) {
+            mConnectedNetworkType = info.getType();
+            mConnectedNetworkTypeName = info.getTypeName();
+        } else {
+            mConnectedNetworkType = ConnectivityManager.TYPE_NONE;
+            mConnectedNetworkTypeName = null;
+        }
+
         int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
 
         if (CHATTY) {
@@ -912,12 +926,13 @@
             //   - We are connected to mobile data, or
             //   - We are not connected to mobile data, as long as the *reason* packets are not
             //     being routed over that link is that we have better connectivity via wifi.
-            // If data is disconnected for some other reason but wifi is connected, we show nothing.
+            // If data is disconnected for some other reason but wifi (or ethernet/bluetooth) 
+            // is connected, we show nothing.
             // Otherwise (nothing connected) we show "No internet connection".
 
             if (mDataConnected) {
                 mobileLabel = mNetworkName;
-            } else if (mWifiConnected) {
+            } else if (mConnected) {
                 if (hasService()) {
                     mobileLabel = mNetworkName;
                 } else {
@@ -997,6 +1012,12 @@
                     R.string.accessibility_bluetooth_tether);
         }
 
+        final boolean ethernetConnected = (mConnectedNetworkType == ConnectivityManager.TYPE_ETHERNET);
+        if (ethernetConnected) {
+            // TODO: icons and strings for Ethernet connectivity
+            combinedLabel = mConnectedNetworkTypeName;
+        }
+
         if (mAirplaneMode &&
                 (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) {
             // Only display the flight-mode icon if not in "emergency calls only" mode.
@@ -1023,7 +1044,7 @@
                 combinedSignalIconId = mDataSignalIconId;
             }
         }
-        else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected) {
+        else if (!mDataConnected && !mWifiConnected && !mBluetoothTethered && !mWimaxConnected && !ethernetConnected) {
             // pretty much totally disconnected
 
             combinedLabel = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
@@ -1224,6 +1245,9 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("NetworkController state:");
+        pw.println(String.format("  %s network type %d (%s)", 
+                mConnected?"CONNECTED":"DISCONNECTED",
+                mConnectedNetworkType, mConnectedNetworkTypeName));
         pw.println("  - telephony ------");
         pw.print("  hasService()=");
         pw.println(hasService());
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index a5190f1..0911f1f 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2968,7 +2968,7 @@
     }
 
     public boolean allowAppAnimationsLw() {
-        if (mKeyguard != null && mKeyguard.isVisibleLw()) {
+        if (mKeyguard != null && mKeyguard.isVisibleLw() && !mKeyguard.isAnimatingLw()) {
             // If keyguard is currently visible, no reason to animate
             // behind it.
             return false;
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 48219a4..2d41f43 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -586,6 +586,10 @@
         mImeSwitcherNotification.defaults = 0; // please be quiet
         mImeSwitcherNotification.sound = null;
         mImeSwitcherNotification.vibrate = null;
+
+        // Tag this notification specially so SystemUI knows it's important
+        mImeSwitcherNotification.kind = new String[] { "android.system.imeswitcher" };
+
         Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
         mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
 
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 6695cb9..f46f8e6 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -618,9 +618,11 @@
                                 PowerManager.PARTIAL_WAKE_LOCK, "Proximity Partial", false);
 
         mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
-        mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mScreenOnIntent.addFlags(
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
         mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
-        mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mScreenOffIntent.addFlags(
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
 
         Resources resources = mContext.getResources();
 
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 26c5c3d..17957d2 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -164,6 +164,9 @@
         if (pendingResults != null) {
             pw.print(prefix); pw.print("pendingResults="); pw.println(pendingResults);
         }
+        if (pendingOptions != null) {
+            pw.print(prefix); pw.print("pendingOptions="); pw.println(pendingOptions);
+        }
         if (uriPermissions != null) {
             if (uriPermissions.readUriPermissions != null) {
                 pw.print(prefix); pw.print("readUriPermissions=");
@@ -202,7 +205,7 @@
         if (lastVisibleTime != 0 || waitingVisible || nowVisible) {
             pw.print(prefix); pw.print("waitingVisible="); pw.print(waitingVisible);
                     pw.print(" nowVisible="); pw.print(nowVisible);
-                    pw.print("lastVisibleTime=");
+                    pw.print(" lastVisibleTime=");
                     TimeUtils.formatDuration(lastVisibleTime, pw); pw.println("");
         }
         if (configDestroy || configChangeFlags != 0) {
@@ -453,6 +456,7 @@
             if (task != null && !finishing) {
                 task.numActivities--;
             }
+            clearOptionsLocked();
         }
     }
 
@@ -466,6 +470,9 @@
             if (task != null && inHistory) {
                 task.numActivities--;
             }
+            if (stopped) {
+                clearOptionsLocked();
+            }
         }
     }
 
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 8a86fe7..e2d6d98 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1055,7 +1055,9 @@
             mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
             r.stopped = true;
             r.state = ActivityState.STOPPED;
-            if (!r.finishing) {
+            if (r.finishing) {
+                r.clearOptionsLocked();
+            } else {
                 if (r.configDestroy) {
                     destroyActivityLocked(r, true, false, "stop-config");
                     resumeTopActivityLocked(null);
@@ -1474,6 +1476,21 @@
             return true;
         }
 
+        // If the most recent activity was noHistory but was only stopped rather
+        // than stopped+finished because the device went to sleep, we need to make
+        // sure to finish it as we're making a new activity topmost.
+        final ActivityRecord last = mLastPausedActivity;
+        if (mService.mSleeping && last != null && !last.finishing) {
+            if ((last.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
+                    || (last.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
+                if (DEBUG_STATES) {
+                    Slog.d(TAG, "no-history finish of " + last + " on new resume");
+                }
+                requestFinishActivityLocked(last.appToken, Activity.RESULT_CANCELED, null,
+                "no-history");
+            }
+        }
+
         if (prev != null && prev != next) {
             if (!prev.waitingVisible && next != null && !next.nowVisible) {
                 prev.waitingVisible = true;
@@ -3279,8 +3296,16 @@
         if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                 || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
             if (!r.finishing) {
-                requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
-                        "no-history");
+                if (!mService.mSleeping) {
+                    if (DEBUG_STATES) {
+                        Slog.d(TAG, "no-history finish of " + r);
+                    }
+                    requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
+                            "no-history");
+                } else {
+                    if (DEBUG_STATES) Slog.d(TAG, "Not finishing noHistory " + r
+                            + " on stop because we're just sleeping");
+                }
             }
         }
 
@@ -3526,9 +3551,10 @@
     final boolean requestFinishActivityLocked(IBinder token, int resultCode,
             Intent resultData, String reason) {
         int index = indexOfTokenLocked(token);
-        if (DEBUG_RESULTS) Slog.v(
+        if (DEBUG_RESULTS || DEBUG_STATES) Slog.v(
                 TAG, "Finishing activity @" + index + ": token=" + token
-                + ", result=" + resultCode + ", data=" + resultData);
+                + ", result=" + resultCode + ", data=" + resultData
+                + ", reason=" + reason);
         if (index < 0) {
             return false;
         }
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 267bf82..758b6e7 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -279,7 +279,7 @@
                         }
                         mService.mFocusMayChange = true;
                     }
-                    if (win.isReadyForDisplay()) {
+                    if (win.isReadyForDisplay() && !winAnimator.isAnimating()) {
                         mForceHiding = true;
                     }
                     if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 6d5ae71..6c8d969 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -885,8 +885,8 @@
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
-        mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
-                "KEEP_SCREEN_ON_FLAG");
+        mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
+                | PowerManager.ON_AFTER_RELEASE, "KEEP_SCREEN_ON_FLAG");
         mHoldingScreenWakeLock.setReferenceCounted(false);
 
         mInputManager = new InputManagerService(context, mInputMonitor);
@@ -3237,7 +3237,7 @@
             if (delayed) {
                 a = new AlphaAnimation(1, 0);
                 a.setStartOffset(0);
-                a.setDuration(delayDuration - 50);
+                a.setDuration(delayDuration - 120);
                 a.setBackgroundColor(0xFF000000);
             } else {
                 a = createExitAnimationLocked(transit, duration);